错误提示如上:
下面看下服务启动流程:
frameworks\base\core\java\android\app\ContextImpl.java
public ComponentName startService(Intent service) { return startServiceCommon(service, false, mUser); }
private ComponentName startServiceCommon(Intent service, boolean requireForeground, UserHandle user) {
ComponentName cn = ActivityManager.getService().startService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded( getContentResolver()), requireForeground, getOpPackageName(), user.getIdentifier()); if (cn != null) { ... } else if (cn.getPackageName().equals("?")) { throw new IllegalStateException( "Not allowed to start service " + service + ": " + cn.getClassName()); } } return cn;
}
通过上面代码可以看出AMS调用startService返回后组件包名为?,就会抛出IllegalStateException异常,下面重点分析AMS。
frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java
public ComponentName startService(IApplicationThread caller, Intent service, String resolvedType, boolean requireForeground, String callingPackage, int userId) throws TransactionTooLargeException {
ComponentName res; try { res = mServices.startServiceLocked(caller, service, resolvedType, callingPid, callingUid, requireForeground, callingPackage, userId); } finally { Binder.restoreCallingIdentity(origId); } return res;
}
frameworks\base\services\core\java\com\android\server\am\ActiveServices.java
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType, int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId) throws TransactionTooLargeException { return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, fgRequired, callingPackage, userId, false); }
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType, int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId, boolean allowBackgroundActivityStarts) throws TransactionTooLargeException { if (forcedStandby || (!r.startRequested && !fgRequired)) { // Before going further -- if this app is not allowed to start services in the // 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) { Slog.w(TAG, "Background start not allowed: service " + service + " to " + r.shortInstanceName + " from pid=" + callingPid + " uid=" + callingUid + " pkg=" + callingPackage + " startFg?=" + fgRequired); // This app knows it is in the new model where this operation is not // allowed, so tell it what has happened. UidRecord uidRec = mAm.mProcessList.getUidRecordLocked(r.appInfo.uid); return new ComponentName("?", "app is in background uid " + uidRec); } }
int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk,
int callingPid, boolean alwaysRestrict, boolean disabledOnly, boolean forcedStandby) {
UidRecord uidRec = mProcessList.getUidRecordLocked(uid);
if (uidRec == null || alwaysRestrict || forcedStandby || uidRec.idle) {
final int startMode = (alwaysRestrict)
? appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk)
: appServicesRestrictedInBackgroundLocked(uid, packageName,
packageTargetSdk);
return startMode;
}
return ActivityManager.APP_START_MODE_NORMAL;
}
int appServicesRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
// Persistent app?
if (mPackageManagerInt.isPackagePersistent(packageName)) {
return ActivityManager.APP_START_MODE_NORMAL;
}
// Non-persistent but background whitelisted?
if (uidOnBackgroundWhitelist(uid)) {
return ActivityManager.APP_START_MODE_NORMAL;
}
// Is this app on the battery whitelist?
if (isOnDeviceIdleWhitelistLocked(uid, /*allowExceptIdleToo=*/ false)) {
return ActivityManager.APP_START_MODE_NORMAL;
}
// None of the service-policy criteria apply, so we apply the common criteria
return appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk);
}
从这里可以看出1.persist app;2.白名单;3.白名单均可以在后台启动服务,下面重点看白名单:
private boolean uidOnBackgroundWhitelist(final int uid) { final int appId = UserHandle.getAppId(uid); final int[] whitelist = mBackgroundAppIdWhitelist; final int N = whitelist.length; for (int i = 0; i < N; i++) { if (appId == whitelist[i]) { return true; } } return false; }
boolean isOnDeviceIdleWhitelistLocked(int uid, boolean allowExceptIdleToo) { final int appId = UserHandle.getAppId(uid); final int[] whitelist = allowExceptIdleToo ? mDeviceIdleExceptIdleWhitelist : mDeviceIdleWhitelist; return Arrays.binarySearch(whitelist, appId) >= 0 || Arrays.binarySearch(mDeviceIdleTempWhitelist, appId) >= 0 || mPendingTempWhitelist.indexOfKey(uid) >= 0; }
可以在相应的白名单中添加自己的app就可以在后台启动服务。