问题背景:客户要求开机能启动第三方利用service。

思路一:写个Receiver监听开机播送,在onReceive中startService。

依照这个思路,再解决startService中碰到的一些问题。
实现大抵如下:
Receiver监听BOOT_COMPLETED这个就比较简单了,大抵代码如下:

//AndroidManifest.xml中申明receiver及权限配置,service申明等    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />        <receiver android:name=".BootReceiver">            <intent-filter>                <action android:name="android.intent.action.BOOT_COMPLETED"/>            </intent-filter>        </receiver>        <service  android:exported="true"             android:name="com.example.servicedemo.BootService">            <intent-filter>                <action android:name="com.example.servicedemo.boot_service"/>            </intent-filter>        </service>//BootReceiver的onReceive办法中启动service:    public void onReceive(Context context, Intent intent) {        context.startService(new Intent(context, BootService.class));    }

碰到的问题就是P上不再容许后盾启动service了。所以在framework中加了一个白名单过滤:

diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.javaindex ca715b5..9df5245 100644— a/services/core/java/com/android/server/am/ActiveServices.java+++ b/services/core/java/com/android/server/am/ActiveServices.java@@ -389,7 +389,7 @@ public final class ActiveServices {ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)throws TransactionTooLargeException {if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service+ if (true) Slog.v(TAG_SERVICE, "startService: " + service+ " type=" + resolvedType + " args=" + service.getExtras());final boolean callerFg;@@ -473,7 +473,11 @@ public final class ActiveServices {// background, then at this point we aren't going to let it period.final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName,r.appInfo.targetSdkVersion, callingPid, false, false, forcedStandby);if (allowed != ActivityManager.APP_START_MODE_NORMAL) { + //add Background start service white list+ boolean isBackgroundStartServiceWhiteList="com.example.servicedemo".equals(callingPackage);+ Slog.d(TAG, "callingPackage = " + callingPackage + " isBackgroundStartServiceWhiteList = " + isBackgroundStartServiceWhiteList);+ //add Background start service white list+ if (allowed != ActivityManager.APP_START_MODE_NORMAL && !isBackgroundStartServiceWhiteList){ Slog.w(TAG, "Background start not allowed: service " + service + " to " + r.name.flattenToShortString() + " from pid=" + callingPid + " uid=" + callingUid

后果客户说监听开机播送再启动service可能会慢,须要尽早启动,同时在启动service的时候还要写SharedPreference,这里就还存在另外一个问题:SharedPreferences in credential encrypted storage are not available until after user is unlocked,具体log如下:

--------- beginning of crash08-26 10:40:16.468  3060  3060 E AndroidRuntime: FATAL EXCEPTION: main08-26 10:40:16.468  3060  3060 E AndroidRuntime: Process: com.example.servicedemo, PID: 306008-26 10:40:16.468  3060  3060 E AndroidRuntime: java.lang.RuntimeException: Unable to create service com.example.servicedemo.BootService: java.lang.IllegalStateException: SharedPreferences in credential encrypted storage are not available until after user is unlocked08-26 10:40:16.468  3060  3060 E AndroidRuntime:     at android.app.ActivityThread.handleCreateService(ActivityThread.java:3544)08-26 10:40:16.468  3060  3060 E AndroidRuntime:     at android.app.ActivityThread.access$1300(ActivityThread.java:199)08-26 10:40:16.468  3060  3060 E AndroidRuntime:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1666)08-26 10:40:16.468  3060  3060 E AndroidRuntime:     at android.os.Handler.dispatchMessage(Handler.java:106)08-26 10:40:16.468  3060  3060 E AndroidRuntime:     at android.os.Looper.loop(Looper.java:193)08-26 10:40:16.468  3060  3060 E AndroidRuntime:     at android.app.ActivityThread.main(ActivityThread.java:6670)08-26 10:40:16.468  3060  3060 E AndroidRuntime:     at java.lang.reflect.Method.invoke(Native Method)08-26 10:40:16.468  3060  3060 E AndroidRuntime:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)08-26 10:40:16.468  3060  3060 E AndroidRuntime:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)08-26 10:40:16.468  3060  3060 E AndroidRuntime: Caused by: java.lang.IllegalStateException: SharedPreferences in credential encrypted storage are not available until after user is unlocked08-26 10:40:16.468  3060  3060 E AndroidRuntime:     at android.app.ContextImpl.getSharedPreferences(ContextImpl.java:419)08-26 10:40:16.468  3060  3060 E AndroidRuntime:     at android.app.ContextImpl.getSharedPreferences(ContextImpl.java:404)08-26 10:40:16.468  3060  3060 E AndroidRuntime:     at android.content.ContextWrapper.getSharedPreferences(ContextWrapper.java:174)08-26 10:40:16.468  3060  3060 E AndroidRuntime:     at com.example.servicedemo.BootService.onCreate(BootService.java:27)08-26 10:40:16.468  3060  3060 E AndroidRuntime:     at android.app.ActivityThread.handleCreateService(ActivityThread.java:3532)08-26 10:40:16.468  3060  3060 E AndroidRuntime:     ... 8 more

这个问题本来也比拟好解决,一来receiver中减少android:directBootAware="true"属性配置,二来Context应用Context directBootContext = appContext.createDeviceProtectedStorageContext()获取。不过客户说receiver代码不能改变,此路不通……
官网:https://developer.android.com...
csdn:https://blog.csdn.net/wuweika...

思路二:framework中想方法间接启动service

在SystemServer.java中有如下代码,能够参考这种写法写一个servicedemo的启动:

            traceBeginAndSlog("StartSystemUI");            try {                startSystemUi(context, windowManagerF);            } catch (Throwable e) {                reportWtf("starting System UI", e);            }            traceEnd();    static final void startSystemUi(Context context, WindowManagerService windowManager) {        Intent intent = new Intent();        intent.setComponent(new ComponentName("com.android.systemui",                    "com.android.systemui.SystemUIService"));        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);        context.startServiceAsUser(intent, UserHandle.SYSTEM);        windowManager.onSystemUiStarted();    }

或者在ActivityManagerService.java的finishBooting办法结尾处startService:

            try {                Slog.i(TAG, "start service");                Intent ii = new Intent();                ii.setClassName("com.example.servicedemo", "com.example.servicedemo.BootService");                mContext.startServiceAsUser(ii, UserHandle.SYSTEM);            } catch (Exception exx) {                Slog.e(TAG, "start error");                exx.printStackTrace();            }

两者成果差不多。
这样启动会报一个错:

10011 09-01 13:21:46.802  2557  2572 W ActivityManager: Unable to start service Intent { cmp=com.example.servicedemo/.BootService } U=0: not found

这个log是在ActiveServices.java的retrieveServiceLocked办法中输入的:

                ResolveInfo rInfo = mAm.getPackageManagerInternalLocked().resolveService(service,                        resolvedType, flags, userId, callingUid);                ServiceInfo sInfo =                    rInfo != null ? rInfo.serviceInfo : null;                if (sInfo == null) {                    Slog.w(TAG_SERVICE, "Unable to start service " + service + " U=" + userId +                          ": not found");                    return null;                }

跟踪startServiceAsUser办法,同时联合报错点resolveService这个办法,梳理出流程:

//[ContextImpl.java][startServiceAsUser]    @Override    public ComponentName startServiceAsUser(Intent service, UserHandle user) {        return startServiceCommon(service, false, user);    }//[ContextImpl.java][startServiceCommon]    private ComponentName startServiceCommon(Intent service, boolean requireForeground,            UserHandle user) {        try {            validateServiceIntent(service);            service.prepareToLeaveProcess(this);            ComponentName cn = ActivityManager.getService().startService(                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(                            getContentResolver()), requireForeground,                            getOpPackageName(), user.getIdentifier());            if (cn != null) {                if (cn.getPackageName().equals("!")) {                    throw new SecurityException(                            "Not allowed to start service " + service                            + " without permission " + cn.getClassName());                } else if (cn.getPackageName().equals("!!")) {                    throw new SecurityException(                            "Unable to start service " + service                            + ": " + cn.getClassName());                } else if (cn.getPackageName().equals("?")) {                    throw new IllegalStateException(                            "Not allowed to start service " + service + ": " + cn.getClassName());                }            }            return cn;        } catch (RemoteException e) {            throw e.rethrowFromSystemServer();        }    }//[ActivityManagerService.java][startService]    @Override    public ComponentName startService(IApplicationThread caller, Intent service,            String resolvedType, boolean requireForeground, String callingPackage, int userId)            throws TransactionTooLargeException {        enforceNotIsolatedCaller("startService");        // Refuse possible leaked file descriptors        if (service != null && service.hasFileDescriptors() == true) {            throw new IllegalArgumentException("File descriptors passed in Intent");        }        if (callingPackage == null) {            throw new IllegalArgumentException("callingPackage cannot be null");        }        if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,                "*** startService: " + service + " type=" + resolvedType + " fg=" + requireForeground);        synchronized(this) {            final int callingPid = Binder.getCallingPid();            final int callingUid = Binder.getCallingUid();            final long origId = Binder.clearCallingIdentity();            ComponentName res;            try {                res = mServices.startServiceLocked(caller, service,                        resolvedType, callingPid, callingUid,                        requireForeground, callingPackage, userId);            } finally {                Binder.restoreCallingIdentity(origId);            }            return res;        }    }//[ActiveServices.java][startServiceLocked]        ServiceLookupResult res =            retrieveServiceLocked(service, resolvedType, callingPackage,                    callingPid, callingUid, userId, true, callerFg, false, false);//[ActiveServices.java][retrieveServiceLocked]                ResolveInfo rInfo = mAm.getPackageManagerInternalLocked().resolveService(service,                        resolvedType, flags, userId, callingUid);                ServiceInfo sInfo =                    rInfo != null ? rInfo.serviceInfo : null;                if (sInfo == null) {                    Slog.w(TAG_SERVICE, "Unable to start service " + service + " U=" + userId +                          ": not found");//这里就是报错的点                    return null;                }//[PackageManagerService.java][resolveService]    @Override    public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) {        Slog.d(TAG, "resolveService userId = " + userId + " intent = " + intent);        final int callingUid = Binder.getCallingUid();        return resolveServiceInternal(intent, resolvedType, flags, userId, callingUid);    }//[PackageManagerService.java][resolveServiceInternal]    private ResolveInfo resolveServiceInternal(Intent intent, String resolvedType, int flags,            int userId, int callingUid) {        Slog.d(TAG, "resolveServiceInternal userId = " + userId + " intent = " + intent);        if (!sUserManager.exists(userId)) {            Slog.e(TAG, "resolveServiceInternal userId NOT exist");            return null;        }        Slog.d(TAG, "resolveServiceInternal updateFlagsForResolve");        flags = updateFlagsForResolve(                flags, userId, intent, callingUid, false /*includeInstantApps*/);        List<ResolveInfo> query = queryIntentServicesInternal(                intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/);//要害函数        if (query != null) {            Slog.d(TAG, "resolveServiceInternal query size = " + query.size());            if (query.size() >= 1) {                // If there is more than one service with the same priority,                // just arbitrarily pick the first one.                return query.get(0);            }        } else {            Slog.d(TAG, "resolveServiceInternal query is null");        }        return null;    }//[PackageManagerService.java][queryIntentServicesInternal]        Slog.d(TAG, "queryIntentServicesInternal comp = " + comp);        if (comp != null) {            final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);            final ServiceInfo si = getServiceInfo(comp, flags, userId);//这里            if (si != null) {                ........            }            ........        }    @Override    public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {        Slog.d(TAG, "getServiceInfo flag = " + flags + " userId = "+ userId);        if (!sUserManager.exists(userId)) {            Slog.d(TAG, "getServiceInfo userId invalid, return");            return null;        }        final int callingUid = Binder.getCallingUid();        flags = updateFlagsForComponent(flags, userId, component);        Slog.d(TAG, "getServiceInfo flag = " + flags + " callingUid = "+ callingUid);        mPermissionManager.enforceCrossUserPermission(callingUid, userId,                false /* requireFullPermission */, false /* checkShell */, "get service info");        synchronized (mPackages) {            PackageParser.Service s = mServices.mServices.get(component);            if (true) Log.v(                TAG, "getServiceInfo " + component + ": " + s);            if (s != null && mSettings.isEnabledAndMatchLPr(s.info, flags, userId)) {//这里                PackageSetting ps = mSettings.mPackages.get(component.getPackageName());                if (ps == null) {                    Slog.e(TAG, "ps null, return");                    return null;                }                if (filterAppAccessLPr(ps, callingUid, component, TYPE_SERVICE, userId)) {                    Slog.e(TAG, "filterAppAccessLPr return");                    return null;                }                Slog.d(TAG, "filterAppAccessLPr return generateServiceInfo");                return PackageParser.generateServiceInfo(                        s, flags, ps.readUserState(userId), userId);            } else {                Slog.e(TAG, "s null");            }        }        Slog.e(TAG, "last return null");        return null;    }//[services/core/java/com/android/server/pm/Settings.java][isEnabledAndMatchLPr]    boolean isEnabledAndMatchLPr(ComponentInfo componentInfo, int flags, int userId) {        final PackageSetting ps = mPackages.get(componentInfo.packageName);        if (ps == null) {            Slog.d(TAG, "isEnabledAndMatchLPr ps null, return");            return false;        }        final PackageUserState userState = ps.readUserState(userId);        boolean isMatch = userState.isMatch(componentInfo, flags);//这里        Slog.d(TAG, "isEnabledAndMatchLPr isMatch ? " + isMatch);        return isMatch;    }///[core/java/android/content/pm/PackageUserState.java][isMatch]    public boolean isMatch(ComponentInfo componentInfo, int flags) {        Slog.d(TAG, "isMatch componentInfo = " + componentInfo);//        if ("com.example.servicedemo".equals(componentInfo.packageName)) {//            Slog.d(TAG, "white list package, return directly");//            return true;//        }        final boolean isSystemApp = componentInfo.applicationInfo.isSystemApp();        final boolean matchUninstalled = (flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0;        if (!isAvailable(flags)                && !(isSystemApp && matchUninstalled)) return false;        if (!isEnabled(componentInfo, flags)) return false;        if ((flags & MATCH_SYSTEM_ONLY) != 0) {            if (!isSystemApp) {                return false;            }        }        final boolean matchesUnaware = ((flags & MATCH_DIRECT_BOOT_UNAWARE) != 0)                && !componentInfo.directBootAware;        final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0)                && componentInfo.directBootAware;        Slog.d(TAG, "isMatch flags: " + flags + " -:" + (flags & MATCH_DIRECT_BOOT_UNAWARE) + " - " + componentInfo.directBootAware);        Slog.d(TAG, "isMatch matchesUnaware: " + matchesUnaware + " matchesAware:" + matchesAware);        Slog.d(TAG, "isMatch return " + (matchesUnaware || matchesAware));        return matchesUnaware || matchesAware;    }

整个流程追踪下来,能看到是PackageUserState.java这个办法返回了false所致。
log如下:

10002 09-01 13:21:46.801  2557  2572 V PackageManager: getServiceInfo ComponentInfo{com.example.servicedemo/com.example.servicedemo.BootService}: Service{58d3746 com.example.servicedemo/.BootService}10003 09-01 13:21:46.801  2557  2572 D PackageUserState: isMatch componentInfo = ServiceInfo{a1c0d07 com.example.servicedemo.BootService}10004 09-01 13:21:46.801  2557  2572 D PackageUserState: isMatch flags: 268960768 -:0 - false10005 09-01 13:21:46.801  2557  2572 D PackageUserState: isMatch matchesUnaware: false matchesAware:false10006 09-01 13:21:46.802  2557  2572 D PackageUserState: isMatch return false10007 09-01 13:21:46.802  2557  2572 D PackageSettings: isEnabledAndMatchLPr isMatch ? false10008 09-01 13:21:46.802  2557  2572 E PackageManager: s null10009 09-01 13:21:46.802  2557  2572 E PackageManager: last return null10011 09-01 13:21:46.802  2557  2572 W ActivityManager: Unable to start service Intent { cmp=com.example.servicedemo/.BootService } U=0: not found

解决方案一个是在这里加上一个过滤白名单,如下面正文掉的代码,另外一个就是在ActivityManagerService的finishBooting中startService时,做个延时解决,如下:

        mHandler.postDelayed(new Runnable() {            @Override            public void run() {            Slog.i(TAG, "finishBooting start service");            try {                Slog.i(TAG, "start service");                Intent ii = new Intent();                ii.setClassName("com.example.servicedemo", "com.example.servicedemo.BootService");                mContext.startServiceAsUser(ii, UserHandle.SYSTEM);            } catch (Exception exx) {                Slog.e(TAG, "start error");                exx.printStackTrace();            }            }        }, 5*1000);