问题背景:客户要求开机能启动第三方利用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);