当前位置:   article > 正文

Android P版本startService Crash问题,深入了解StartService流程_targets o+, restricted

targets o+, restricted

上个礼拜刘神在微信上跟我探讨人生的时候突然提到,小高你的博客很久没更新了。我心里一惊,是啊已经一年没更新博客了。但是写点什么呢?刘神说跟你讲下我最近遇到一个很有意思的bug,也许你就有想法了

 

BUG现象

当你在activity里面中delay(这里可以用handler或者各种延时方法)启动service时,按home键回到桌面,如果delay的时间为70s,那么程序会报错,但是如果delay的时间为50s,却可以正常运行

 

相信聪明的同学肯定已经心中有数了,google果然对安全性越来越看重了,很明显google这是在限制后台app 只能在一定的时间内startService,那么这个问题我们该怎么去解决呢,我们可以去看下google官方有没有相应解释,以及将startService相应流程看一遍,相信问题自然迎刃而解

 

Google官方解释

https://developer.android.google.cn/about/versions/oreo/background.html#services

 

StartService流程

启动Service的第一步就是Activity.startService()那我们去看看Activity发现里面竟然没有startService()这个方法!why?去父类ContextThemeWrapper找找,还是没有?再去爷爷类ContextWrapper找找,果然在爷爷类这里。这里涉及到java的继承写法,所谓继承,是一个很简单很直观的概念,与显示生活中的继承(例如儿子继承了父亲财产)类似。继承可以理解为一个类从另一个类中获取方法和属性的过程。如果类B继承于类A,那么类B就拥有类A的属性和方法。这也就是为什么Activity中并没有写startService()却可以正常调用

frameworks\base\core\java\android\content\ContextWrapper.java

  1. Context mBase;
  2. @Override
  3. public ComponentName startService(Intent service) {
  4. return mBase.startService(service);
  5. }

那么我们来看下Activity继承的这个方法,可以看到方法里面还是没有具体的内容,而是继续往下调用了mBase.startService(service),mBase.startService(service) 中的mBase其实是一个Context,熟悉源码的同学应该知道,Context是一个抽象类,所以这里实际上传进来的Context的实现类ContextImpl,也就是说调用的是ContextImpl里面的startService(service)方法,有些同学可能会不理解,为什么明明声明是Context,却说调用的是ContextImpl里面的的方法呢?

其实这几个类设计是典型的装饰模式(Decorator),ContextWrapper的startService函数调用ContextImpl的startService额外职责后,可以继续做自己的事情。而这里之所以不声明为ContextImpl的原因则是为了让代码更灵活一些,比如说以后要继续加个ContextImpl2、ContextImpl3会更方便一些

frameworks\base\core\java\android\app\ContextImpl.java

  1. @Override
  2. public ComponentName startService(Intent service) {
  3. warnIfCallingFromSystemProcess();
  4. return startServiceCommon(service, false, mUser);
  5. }

可以看到ContextImpl.startService()继续往下调用了 startServiceCommon(service, false, mUser);

  1. private ComponentName startServiceCommon(Intent service, boolean requireForeground,
  2. UserHandle user) {
  3. try {
  4. validateServiceIntent(service);
  5. service.prepareToLeaveProcess(this);
  6. ComponentName cn = ActivityManager.getService().startService(
  7. mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
  8. getContentResolver()), requireForeground,
  9. getOpPackageName(), user.getIdentifier());
  10. if (cn != null) {
  11. if (cn.getPackageName().equals("!")) {
  12. throw new SecurityException(
  13. "Not allowed to start service " + service
  14. + " without permission " + cn.getClassName());
  15. } else if (cn.getPackageName().equals("!!")) {
  16. throw new SecurityException(
  17. "Unable to start service " + service
  18. + ": " + cn.getClassName());
  19. } else if (cn.getPackageName().equals("?")) {
  20. throw new IllegalStateException(
  21. "Not allowed to start service " + service + ": " + cn.getClassName());
  22. }
  23. }
  24. return cn;
  25. } catch (RemoteException e) {
  26. throw e.rethrowFromSystemServer();
  27. }
  28. }

 

throw new IllegalStateException("Not allowed to start service " + service + ": " + cn.getClassName());

这个startServiceCommon方法我们可以注意一下这行,再对比下我们得到的异常log,可以发现跟异常log是一样的。那么我们可以推断异常就是这里抛出来的,那么为什么cn.getPackageName().equals("?") 这个条件会成立呢 我们继续往下看 

frameworks\base\core\java\android\app\ActivityManager.java

  1.    public static IActivityManager getService() {
  2.         return IActivityManagerSingleton.get();
  3.     }
  4.     private static final Singleton<IActivityManager> IActivityManagerSingleton =
  5.             new Singleton<IActivityManager>() {
  6.                 @Override
  7.                 protected IActivityManager create() {
  8.                     final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
  9.                     final IActivityManager am = IActivityManager.Stub.asInterface(b);
  10.                     return am;
  11.                 }
  12.             };

这里用到了一个叫Singleton的类,也就是我们常说的单例,主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。 使用Singleton的好处还在于可以节省内存

final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);返回的是ams

final IActivityManager am = IActivityManager.Stub.asInterface(b); 返回一个IActivityManager供我们做一些工作 这里用到Android的特色通信方式aidl 使用Binder驱动走到ams里面,所以其实IActivityManager 真正实现是在ActivityManagerService里面,AIDL是一个缩写,全称是Android Interface Definition Language,也就是Android接口定义语言,这是我们在开发Android时经常要用到的

 

frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java

  1.     @Override
  2.     public ComponentName startService(IApplicationThread caller, Intent service,
  3.             String resolvedType, boolean requireForeground, String callingPackage, int userId)
  4.             throws TransactionTooLargeException {
  5.         enforceNotIsolatedCaller("startService");
  6.         // Refuse possible leaked file descriptors
  7.         if (service != null && service.hasFileDescriptors() == true) {
  8.             throw new IllegalArgumentException("File descriptors passed in Intent");
  9.         }
  10.         if (callingPackage == null) {
  11.             throw new IllegalArgumentException("callingPackage cannot be null");
  12.         }
  13.         if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
  14.                 "*** startService: " + service + " type=" + resolvedType + " fg=" + requireForeground);
  15.         synchronized(this) {
  16.             final int callingPid = Binder.getCallingPid();
  17.             final int callingUid = Binder.getCallingUid();
  18.             final long origId = Binder.clearCallingIdentity();
  19.             ComponentName res;
  20.             try {
  21.                 res = mServices.startServiceLocked(caller, service,
  22.                         resolvedType, callingPid, callingUid,
  23.                         requireForeground, callingPackage, userId);
  24.             } finally {
  25.                 Binder.restoreCallingIdentity(origId);
  26.             }
  27.             return res;
  28.         }
  29. }

然后下一步到了ActivityManagerService.startService(),继续往下调用

mServices.startServiceLocked(caller, service,resolvedType, callingPid, callingUid,requireForeground, callingPackage, userId);

这里的mService的类型其实是ActiveServices

 

frameworks\base\services\core\java\com\android\server\am\ActiveServices.java

  1. ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
  2. int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
  3. throws TransactionTooLargeException {
  4. if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
  5. + " type=" + resolvedType + " args=" + service.getExtras());
  6. final boolean callerFg;
  7. if (caller != null) {
  8. final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
  9. if (callerApp == null) {
  10. throw new SecurityException(
  11. "Unable to find app for caller " + caller
  12. + " (pid=" + callingPid
  13. + ") when starting service " + service);
  14. }
  15. callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
  16. } else {
  17. callerFg = true;
  18. }
  19. android.util.Log.e("gaoshifeng","callerFg == " + callerFg);
  20. ServiceLookupResult res =
  21. retrieveServiceLocked(service, resolvedType, callingPackage,
  22. callingPid, callingUid, userId, true, callerFg, false, false);
  23. if (res == null) {
  24. return null;
  25. }
  26. if (res.record == null) {
  27. return new ComponentName("!", res.permission != null
  28. ? res.permission : "private to package");
  29. }
  30. ServiceRecord r = res.record;
  31. if (!mAm.mUserController.exists(r.userId)) {
  32. Slog.w(TAG, "Trying to start service with non-existent user! " + r.userId);
  33. return null;
  34. }
  35. // If we're starting indirectly (e.g. from PendingIntent), figure out whether
  36. // we're launching into an app in a background state. This keys off of the same
  37. // idleness state tracking as e.g. O+ background service start policy.
  38. final boolean bgLaunch = !mAm.isUidActiveLocked(r.appInfo.uid);
  39. // If the app has strict background restrictions, we treat any bg service
  40. // start analogously to the legacy-app forced-restrictions case, regardless
  41. // of its target SDK version.
  42. boolean forcedStandby = false;
  43. if (bgLaunch && appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) {
  44. if (DEBUG_FOREGROUND_SERVICE) {
  45. Slog.d(TAG, "Forcing bg-only service start only for " + r.shortName
  46. + " : bgLaunch=" + bgLaunch + " callerFg=" + callerFg);
  47. }
  48. forcedStandby = true;
  49. }
  50. // If this is a direct-to-foreground start, make sure it is allowed as per the app op.
  51. boolean forceSilentAbort = false;
  52. if (fgRequired) {
  53. final int mode = mAm.mAppOpsService.checkOperation(
  54. AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
  55. switch (mode) {
  56. case AppOpsManager.MODE_ALLOWED:
  57. case AppOpsManager.MODE_DEFAULT:
  58. // All okay.
  59. break;
  60. case AppOpsManager.MODE_IGNORED:
  61. // Not allowed, fall back to normal start service, failing siliently
  62. // if background check restricts that.
  63. Slog.w(TAG, "startForegroundService not allowed due to app op: service "
  64. + service + " to " + r.name.flattenToShortString()
  65. + " from pid=" + callingPid + " uid=" + callingUid
  66. + " pkg=" + callingPackage);
  67. fgRequired = false;
  68. forceSilentAbort = true;
  69. break;
  70. default:
  71. return new ComponentName("!!", "foreground not allowed as per app op");
  72. }
  73. }
  74. // If this isn't a direct-to-foreground start, check our ability to kick off an
  75. // arbitrary service
  76. android.util.Log.e("gaoshifeng","forcedStandby = " + forcedStandby);
  77. android.util.Log.e("gaoshifeng","!r.startRequested = " + !r.startRequested);//问题在这
  78. android.util.Log.e("gaoshifeng","!fgRequired = " + !fgRequired);
  79. if (forcedStandby || (!r.startRequested && !fgRequired)) {
  80. // Before going further -- if this app is not allowed to start services in the
  81. // background, then at this point we aren't going to let it period.
  82. final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName,
  83. r.appInfo.targetSdkVersion, callingPid, false, false, forcedStandby);
  84. if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
  85. Slog.w(TAG, "Background start not allowed: service "
  86. + service + " to " + r.name.flattenToShortString()
  87. + " from pid=" + callingPid + " uid=" + callingUid
  88. + " pkg=" + callingPackage + " startFg?=" + fgRequired);
  89. if (allowed == ActivityManager.APP_START_MODE_DELAYED || forceSilentAbort) {
  90. // In this case we are silently disabling the app, to disrupt as
  91. // little as possible existing apps.
  92. return null;
  93. }
  94. if (forcedStandby) {
  95. // This is an O+ app, but we might be here because the user has placed
  96. // it under strict background restrictions. Don't punish the app if it's
  97. // trying to do the right thing but we're denying it for that reason.
  98. if (fgRequired) {
  99. if (DEBUG_BACKGROUND_CHECK) {
  100. Slog.v(TAG, "Silently dropping foreground service launch due to FAS");
  101. }
  102. return null;
  103. }
  104. }
  105. // This app knows it is in the new model where this operation is not
  106. // allowed, so tell it what has happened.
  107. UidRecord uidRec = mAm.mActiveUids.get(r.appInfo.uid);
  108. return new ComponentName("?", "app is in background uid " + uidRec);
  109. }
  110. }
  111. // At this point we've applied allowed-to-start policy based on whether this was
  112. // an ordinary startService() or a startForegroundService(). Now, only require that
  113. // the app follow through on the startForegroundService() -> startForeground()
  114. // contract if it actually targets O+.
  115. if (r.appInfo.targetSdkVersion < Build.VERSION_CODES.O && fgRequired) {
  116. if (DEBUG_BACKGROUND_CHECK || DEBUG_FOREGROUND_SERVICE) {
  117. Slog.i(TAG, "startForegroundService() but host targets "
  118. + r.appInfo.targetSdkVersion + " - not requiring startForeground()");
  119. }
  120. fgRequired = false;
  121. }
  122. NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked(
  123. callingUid, r.packageName, service, service.getFlags(), null, r.userId);
  124. // If permissions need a review before any of the app components can run,
  125. // we do not start the service and launch a review activity if the calling app
  126. // is in the foreground passing it a pending intent to start the service when
  127. // review is completed.
  128. if (mAm.mPermissionReviewRequired) {
  129. // XXX This is not dealing with fgRequired!
  130. if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage,
  131. callingUid, service, callerFg, userId)) {
  132. return null;
  133. }
  134. }
  135. if (unscheduleServiceRestartLocked(r, callingUid, false)) {
  136. if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "START SERVICE WHILE RESTART PENDING: " + r);
  137. }
  138. r.lastActivity = SystemClock.uptimeMillis();
  139. r.startRequested = true;
  140. r.delayedStop = false;
  141. r.fgRequired = fgRequired;
  142. r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
  143. service, neededGrants, callingUid));
  144. if (fgRequired) {
  145. // We are now effectively running a foreground service.
  146. mAm.mAppOpsService.startOperation(AppOpsManager.getToken(mAm.mAppOpsService),
  147. AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, true);
  148. }
  149. final ServiceMap smap = getServiceMapLocked(r.userId);
  150. boolean addToStarting = false;
  151. if (!callerFg && !fgRequired && r.app == null
  152. && mAm.mUserController.hasStartedUserState(r.userId)) {
  153. ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
  154. if (proc == null || proc.curProcState > ActivityManager.PROCESS_STATE_RECEIVER) {
  155. // If this is not coming from a foreground caller, then we may want
  156. // to delay the start if there are already other background services
  157. // that are starting. This is to avoid process start spam when lots
  158. // of applications are all handling things like connectivity broadcasts.
  159. // We only do this for cached processes, because otherwise an application
  160. // can have assumptions about calling startService() for a service to run
  161. // in its own process, and for that process to not be killed before the
  162. // service is started. This is especially the case for receivers, which
  163. // may start a service in onReceive() to do some additional work and have
  164. // initialized some global state as part of that.
  165. if (DEBUG_DELAYED_SERVICE) Slog.v(TAG_SERVICE, "Potential start delay of "
  166. + r + " in " + proc);
  167. if (r.delayed) {
  168. // This service is already scheduled for a delayed start; just leave
  169. // it still waiting.
  170. if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Continuing to delay: " + r);
  171. return r.name;
  172. }
  173. if (smap.mStartingBackground.size() >= mMaxStartingBackground) {
  174. // Something else is starting, delay!
  175. Slog.i(TAG_SERVICE, "Delaying start of: " + r);
  176. smap.mDelayedStartList.add(r);
  177. r.delayed = true;
  178. return r.name;
  179. }
  180. if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Not delaying: " + r);
  181. addToStarting = true;
  182. } else if (proc.curProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
  183. // We slightly loosen when we will enqueue this new service as a background
  184. // starting service we are waiting for, to also include processes that are
  185. // currently running other services or receivers.
  186. addToStarting = true;
  187. if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
  188. "Not delaying, but counting as bg: " + r);
  189. } else if (DEBUG_DELAYED_STARTS) {
  190. StringBuilder sb = new StringBuilder(128);
  191. sb.append("Not potential delay (state=").append(proc.curProcState)
  192. .append(' ').append(proc.adjType);
  193. String reason = proc.makeAdjReason();
  194. if (reason != null) {
  195. sb.append(' ');
  196. sb.append(reason);
  197. }
  198. sb.append("): ");
  199. sb.append(r.toString());
  200. Slog.v(TAG_SERVICE, sb.toString());
  201. }
  202. } else if (DEBUG_DELAYED_STARTS) {
  203. if (callerFg || fgRequired) {
  204. Slog.v(TAG_SERVICE, "Not potential delay (callerFg=" + callerFg + " uid="
  205. + callingUid + " pid=" + callingPid + " fgRequired=" + fgRequired + "): " + r);
  206. } else if (r.app != null) {
  207. Slog.v(TAG_SERVICE, "Not potential delay (cur app=" + r.app + "): " + r);
  208. } else {
  209. Slog.v(TAG_SERVICE,
  210. "Not potential delay (user " + r.userId + " not started): " + r);
  211. }
  212. }
  213. ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
  214. return cmp;
  215. }

可以看到这一行,这就可以解释为什么前面cn.getPackageName().equals("?") 这个条件会成立了,也就是我们抛异常的真正原因

return new ComponentName("?", "app is in background uid " + uidRec);

最后调用startServiceInnerLocked()继续往下走

startServiceInnerLocked()-->bringUpServiceLocked() -->realStartServiceLocked() 

 

  1. app.thread.scheduleCreateService(r, r.serviceInfo,
  2. mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
  3. app.repProcState);

  realStartServiceLocked() 里面调用app.thread.scheduleCreateService()进入启动流程,app其实是ProcessRecord类型我们找到ProcessRecord.java可以看到thread就是IApplicationThread,而 IApplicationThread就是ActivityThread的内部类,那就好办了,我们找到ActivityThread.java 找到scheduleCreateService方法

\frameworks\base\core\java\android\app\ActivityThread.java

  1. public final void scheduleCreateService(IBinder token,
  2. ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
  3. updateProcessState(processState, false);
  4. CreateServiceData s = new CreateServiceData();
  5. s.token = token;
  6. s.info = info;
  7. s.compatInfo = compatInfo;
  8. sendMessage(H.CREATE_SERVICE, s);
  9. }

 

  1. case CREATE_SERVICE:
  2. Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));
  3. handleCreateService((CreateServiceData)msg.obj);
  4. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
  5. break;
  1. private void handleCreateService(CreateServiceData data) {
  2. // If we are getting ready to gc after going to the background, well
  3. // we are back active so skip it.
  4. unscheduleGcIdler();
  5. LoadedApk packageInfo = getPackageInfoNoCheck(
  6. data.info.applicationInfo, data.compatInfo);
  7. Service service = null;
  8. try {
  9. java.lang.ClassLoader cl = packageInfo.getClassLoader();
  10. service = packageInfo.getAppFactory()
  11. .instantiateService(cl, data.info.name, data.intent);
  12. } catch (Exception e) {
  13. if (!mInstrumentation.onException(service, e)) {
  14. throw new RuntimeException(
  15. "Unable to instantiate service " + data.info.name
  16. + ": " + e.toString(), e);
  17. }
  18. }
  19. try {
  20. if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
  21. ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
  22. context.setOuterContext(service);
  23. Application app = packageInfo.makeApplication(false, mInstrumentation);
  24. service.attach(context, this, data.info.name, data.token, app,
  25. ActivityManager.getService());
  26. service.onCreate();//到这一个service 就被create出来了
  27. mServices.put(data.token, service);
  28. try {
  29. ActivityManager.getService().serviceDoneExecuting(
  30. data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
  31. } catch (RemoteException e) {
  32. throw e.rethrowFromSystemServer();
  33. }
  34. } catch (Exception e) {
  35. if (!mInstrumentation.onException(service, e)) {
  36. throw new RuntimeException(
  37. "Unable to create service " + data.info.name
  38. + ": " + e.toString(), e);
  39. }
  40. }
  41. }

最终会调用到handleCreateService(),看到我们熟悉的service.onCreate();也就意味着整个startService的流程我们就走完了

 

BUG分析

 

那我们回过头来看看我们遇到的bug,刚刚我们已经知道问题产生的位置就是这里,为了方便阅读我就直接把变量名字改成类名好了。我们来仔细看看这一部分代码

ActiveServices.startServiceLocked()中的

  1. android.util.Log.e("gaoshifeng","forcedStandby = " + forcedStandby);
  2. android.util.Log.e("gaoshifeng","!r.startRequested = " + !r.startRequested);//问题在这
  3. android.util.Log.e("gaoshifeng","!fgRequired = " + !fgRequired);
  4. if (forcedStandby || (!r.startRequested && !fgRequired)) {
  5. // Before going further -- if this app is not allowed to start services in the
  6. // background, then at this point we aren't going to let it period.
  7. final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName,
  8. r.appInfo.targetSdkVersion, callingPid, false, false, forcedStandby);
  9. if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
  10. Slog.w(TAG, "Background start not allowed: service "
  11. + service + " to " + r.name.flattenToShortString()
  12. + " from pid=" + callingPid + " uid=" + callingUid
  13. + " pkg=" + callingPackage + " startFg?=" + fgRequired);
  14. if (allowed == ActivityManager.APP_START_MODE_DELAYED || forceSilentAbort) {
  15. // In this case we are silently disabling the app, to disrupt as
  16. // little as possible existing apps.
  17. return null;
  18. }
  19. if (forcedStandby) {
  20. // This is an O+ app, but we might be here because the user has placed
  21. // it under strict background restrictions. Don't punish the app if it's
  22. // trying to do the right thing but we're denying it for that reason.
  23. if (fgRequired) {
  24. if (DEBUG_BACKGROUND_CHECK) {
  25. Slog.v(TAG, "Silently dropping foreground service launch due to FAS");
  26. }
  27. return null;
  28. }
  29. }
  30. // This app knows it is in the new model where this operation is not
  31. // allowed, so tell it what has happened.
  32. UidRecord uidRec = mAm.mActiveUids.get(r.appInfo.uid);
  33. return new ComponentName("?", "app is in background uid " + uidRec);
  34. }
  35. }

大家都知道解决bug,最好是需要配合一些log来看效率会更高,这边跟大家分享一个小技巧,在调试Service这块的时候,其实很多系统log他是默认不打印的,我们可以把它全打印出来这样有助于我们debug,Service这部分的开关就是在ActivityManagerDebugConfig.java里面有个DEBUG_ALL ,我们把它设置为true这样就可以打印所有log了frameworks\base\services\core\java\com\android\server\am\ActivityManagerDebugConfig.java

  1. // Enable all debug log categories.
  2. public static boolean DEBUG_ALL = true;

 

回到代码我们可以看到当allowed != ActivityManager.APP_START_MODE_NORMAL时

return new ComponentName("?", "app is in background uid " + uidRec);

也就是name里面就会包含“?”,那么我们来看看allowed这个变量是如何获取的

final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName,

                    r.appInfo.targetSdkVersion, callingPid, false, false, forcedStandby);

  1. int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk,
  2. int callingPid, boolean alwaysRestrict, boolean disabledOnly, boolean forcedStandby) {
  3. UidRecord uidRec = mActiveUids.get(uid);
  4. if (DEBUG_BACKGROUND_CHECK) Slog.d(TAG, "checkAllowBackground: uid=" + uid + " pkg="
  5. + packageName + " rec=" + uidRec + " always=" + alwaysRestrict + " idle="
  6. + (uidRec != null ? uidRec.idle : false));
  7. if (uidRec == null || alwaysRestrict || forcedStandby || uidRec.idle) {
  8. boolean ephemeral;
  9. if (uidRec == null) {
  10. ephemeral = getPackageManagerInternalLocked().isPackageEphemeral(
  11. UserHandle.getUserId(uid), packageName);
  12. } else {
  13. ephemeral = uidRec.ephemeral;
  14. }
  15. if (ephemeral) {
  16. // We are hard-core about ephemeral apps not running in the background.
  17. return ActivityManager.APP_START_MODE_DISABLED;
  18. } else {
  19. if (disabledOnly) {
  20. // The caller is only interested in whether app starts are completely
  21. // disabled for the given package (that is, it is an instant app). So
  22. // we don't need to go further, which is all just seeing if we should
  23. // apply a "delayed" mode for a regular app.
  24. return ActivityManager.APP_START_MODE_NORMAL;
  25. }
  26. final int startMode = (alwaysRestrict)
  27. ? appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk)
  28. : appServicesRestrictedInBackgroundLocked(uid, packageName,
  29. packageTargetSdk);
  30. if (DEBUG_BACKGROUND_CHECK) {
  31. Slog.d(TAG, "checkAllowBackground: uid=" + uid
  32. + " pkg=" + packageName + " startMode=" + startMode
  33. + " onwhitelist=" + isOnDeviceIdleWhitelistLocked(uid, false)
  34. + " onwhitelist(ei)=" + isOnDeviceIdleWhitelistLocked(uid, true));
  35. }
  36. if (startMode == ActivityManager.APP_START_MODE_DELAYED) {
  37. // This is an old app that has been forced into a "compatible as possible"
  38. // mode of background check. To increase compatibility, we will allow other
  39. // foreground apps to cause its services to start.
  40. if (callingPid >= 0) {
  41. ProcessRecord proc;
  42. synchronized (mPidsSelfLocked) {
  43. proc = mPidsSelfLocked.get(callingPid);
  44. }
  45. if (proc != null &&
  46. !ActivityManager.isProcStateBackground(proc.curProcState)) {
  47. // Whoever is instigating this is in the foreground, so we will allow it
  48. // to go through.
  49. return ActivityManager.APP_START_MODE_NORMAL;
  50. }
  51. }
  52. }
  53. return startMode;
  54. }
  55. }
  56. return ActivityManager.APP_START_MODE_NORMAL;
  57. }

可以看到这里就有一些系统log可以协助我们来分析

  1.         if (DEBUG_BACKGROUND_CHECK) Slog.d(TAG, "checkAllowBackground: uid=" + uid + " pkg="
  2.                 + packageName + " rec=" + uidRec + " always=" + alwaysRestrict + " idle="
  3.                 + (uidRec != null ? uidRec.idle : false));
  4.         if (uidRec == null || alwaysRestrict || forcedStandby || uidRec.idle) {

 

 

 

抛出异常时的log

  1. 07-29 22:37:41.155 1128 1147 I ActivityManager: UID idle uid=10091
  2. 07-29 22:37:47.567 1128 6869 V ActivityManager: Broadcast sticky: Intent { act=android.intent.action.SIG_STR flg=0x10 (has extras) } ordered=false userid=-1
  3. 07-29 22:37:47.568 1128 6869 V ActivityManager: Enqueueing broadcast: android.intent.action.SIG_STR replacePending=false
  4. 07-29 22:37:48.488 1128 6869 V ActivityManager: *** startService: Intent { cmp=com.example.illa.testff/.MyService } type=null fg=false
  5. 07-29 22:37:48.489 1128 6869 V ActivityManager: startService: Intent { cmp=com.example.illa.testff/.MyService } type=null args=null
  6. 07-29 22:37:46.705 4340 4340 I chatty : uid=0(root) kworker/3:1 identical 2 lines
  7. 07-29 22:37:47.705 4340 4340 W kworker/3:1: type=1400 audit(0.0:1042): avc: denied { search } for name="battery" dev="sysfs" ino=7493 scontext=u:r:kernel:s0 tcontext=u:object_r:sysfs_batteryinfo:s0 tclass=dir permissive=0
  8. 07-29 22:37:48.489 1128 6869 E gaoshifeng: callerFg == false
  9. 07-29 22:37:48.489 1128 6869 V ActivityManager: retrieveServiceLocked: Intent { cmp=com.example.illa.testff/.MyService } type=null callingUid=10091
  10. 07-29 22:37:48.490 1128 6869 V ActivityManager: Retrieve created new service: ServiceRecord{7d1fb58 u0 com.example.illa.testff/.MyService}
  11. 07-29 22:37:48.491 1128 6869 E gaoshifeng: forcedStandby = false
  12. 07-29 22:37:48.491 1128 6869 E gaoshifeng: !r.startRequested = true
  13. 07-29 22:37:48.491 1128 6869 E gaoshifeng: !fgRequired = true
  14. 07-29 22:37:48.491 1128 6869 D ActivityManager: checkAllowBackground: uid=10091 pkg=com.example.illa.testff rec=UidRecord{5495f47 u0a91 LAST bg:+1m7s354ms idle change:idle procs:1 seq(0,0,0)} always=false idle=true
  15. 07-29 22:37:48.492 1128 6869 I ActivityManager: App 10091/com.example.illa.testff targets O+, restricted
  16. 07-29 22:37:48.492 1128 6869 D ActivityManager: checkAllowBackground: uid=10091 pkg=com.example.illa.testff startMode=2 onwhitelist=false onwhitelist(ei)=false
  17. 07-29 22:37:48.492 1128 6869 W ActivityManager: Background start not allowed: service Intent { cmp=com.example.illa.testff/.MyService } to com.example.illa.testff/.MyService from pid=7287 uid=10091 pkg=com.example.illa.testff startFg?=false
  18. 07-29 22:37:48.497 7287 7287 D AndroidRuntime: Shutting down VM
  19. --------- beginning of crash
  20. 07-29 22:37:48.511 7287 7287 E AndroidRuntime: FATAL EXCEPTION: main
  21. 07-29 22:37:48.511 7287 7287 E AndroidRuntime: Process: com.example.illa.testff, PID: 7287
  22. 07-29 22:37:48.511 7287 7287 E AndroidRuntime: java.lang.IllegalStateException: Not allowed to start service Intent { cmp=com.example.illa.testff/.MyService }: app is in background uid UidRecord{5495f47 u0a91 LAST bg:+1m7s355ms idle change:idle procs:1 seq(0,0,0)}
  23. 07-29 22:37:48.511 7287 7287 E AndroidRuntime: at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1578)
  24. 07-29 22:37:48.511 7287 7287 E AndroidRuntime: at android.app.ContextImpl.startService(ContextImpl.java:1533)
  25. 07-29 22:37:48.511 7287 7287 E AndroidRuntime: at android.content.ContextWrapper.startService(ContextWrapper.java:664)
  26. 07-29 22:37:48.511 7287 7287 E AndroidRuntime: at com.example.illa.testff.MainActivity$1.run(MainActivity.java:39)
  27. 07-29 22:37:48.511 7287 7287 E AndroidRuntime: at android.os.Handler.handleCallback(Handler.java:873)
  28. 07-29 22:37:48.511 7287 7287 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:99)
  29. 07-29 22:37:48.511 7287 7287 E AndroidRuntime: at android.os.Looper.loop(Looper.java:193)
  30. 07-29 22:37:48.511 7287 7287 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:6702)
  31. 07-29 22:37:48.511 7287 7287 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
  32. 07-29 22:37:48.511 7287 7287 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
  33. 07-29 22:37:48.511 7287 7287 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:911)
  34. 07-29 22:37:48.531 1128 6869 W ActivityManager: Force finishing activity com.example.illa.testff/.MainActivity

 

正常运行log

  1. 07-30 02:00:19.821 1128 1930 V ActivityManager: *** startService: Intent { cmp=com.example.illa.testff/.MyService } type=null fg=false
  2. 07-30 02:00:19.821 1128 1930 V ActivityManager: startService: Intent { cmp=com.example.illa.testff/.MyService } type=null args=null
  3. 07-30 02:00:19.821 1128 1930 E gaoshifeng: callerFg == false
  4. 07-30 02:00:19.821 1128 1930 V ActivityManager: retrieveServiceLocked: Intent { cmp=com.example.illa.testff/.MyService } type=null callingUid=10091
  5. 07-30 02:00:19.822 1128 1930 V ActivityManager: Retrieve created new service: ServiceRecord{8666a9a u0 com.example.illa.testff/.MyService}
  6. 07-30 02:00:19.822 1128 1930 E gaoshifeng: forcedStandby = false
  7. 07-30 02:00:19.822 1128 1930 E gaoshifeng: !r.startRequested = true
  8. 07-30 02:00:19.822 1128 1930 E gaoshifeng: !fgRequired = true
  9. 07-30 02:00:19.822 1128 1930 D ActivityManager: checkAllowBackground: uid=10091 pkg=com.example.illa.testff rec=UidRecord{5999804 u0a91 LAST bg:+20s513ms change:cached procs:1 seq(0,0,0)} always=false idle=false
  10. 07-30 02:00:19.823 1128 1930 V ActivityManager: Checking URI perm to data=null clip=null from Intent { cmp=com.example.illa.testff/.MyService }; flags=0x0
  11. 07-30 02:00:19.823 1128 1930 V ActivityManager: Potential start delay of ServiceRecord{8666a9a u0 com.example.illa.testff/.MyService} in ProcessRecord{2b66907 13759:com.example.illa.testff/u0a91}
  12. 07-30 02:00:19.823 1128 1930 V ActivityManager: Not delaying: ServiceRecord{8666a9a u0 com.example.illa.testff/.MyService}
  13. 07-30 02:00:19.823 1128 1930 V ActivityManager: Bringing up ServiceRecord{8666a9a u0 com.example.illa.testff/.MyService} android.content.Intent$FilterComparison@ff445089 fg=false
  14. 07-30 02:00:19.823 1128 1930 V ActivityManager_MU: bringUpServiceLocked: appInfo.uid=10091 app=ProcessRecord{2b66907 13759:com.example.illa.testff/u0a91}
  15. 07-30 02:00:19.824 1128 1930 V ActivityManager_MU: realStartServiceLocked, ServiceRecord.uid = 10091, ProcessRecord.uid = 10091
  16. 07-30 02:00:19.824 1128 1930 V ActivityManager: >>> EXECUTING create of ServiceRecord{8666a9a u0 com.example.illa.testff/.MyService} in app ProcessRecord{2b66907 13759:com.example.illa.testff/u0a91}

 

07-29 22:37:48.491  1128  6869 D ActivityManager: checkAllowBackground: uid=10091 pkg=com.example.illa.testff rec=UidRecord{5495f47 u0a91 LAST bg:+1m7s354ms idle change:idle procs:1 seq(0,0,0)} always=false idle=true
07-29 22:37:48.492  1128  6869 I ActivityManager: App 10091/com.example.illa.testff targets O+, restricted
07-29 22:37:48.492  1128  6869 D ActivityManager: checkAllowBackground: uid=10091 pkg=com.example.illa.testff startMode=2 onwhitelist=false onwhitelist(ei)=false
 

 

07-30 02:00:19.822  1128  1930 D ActivityManager: checkAllowBackground: uid=10091 pkg=com.example.illa.testff rec=UidRecord{5999804 u0a91 LAST bg:+20s513ms change:cached procs:1 seq(0,0,0)} always=false idle=false

 


我们对比一下log的差异可以看到这个地方比较奇怪,发生异常的log idle=true而且多打印了一行log,找到打印后面这行log的位置

  1. int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
  2. // Apps that target O+ are always subject to background check
  3. if (packageTargetSdk >= Build.VERSION_CODES.O) {
  4. if (DEBUG_BACKGROUND_CHECK) {
  5. Slog.i(TAG, "App " + uid + "/" + packageName + " targets O+, restricted");
  6. }
  7. return ActivityManager.APP_START_MODE_DELAYED_RIGID;
  8. }

而这个appRestrictedInBackgroundLocked()方法正是getAppStartModeLocked()方法里面当uidRec.idle=true时调用的,正是当uidRec.idle=true 便会成立,从而导致我们log打印的差异

return ActivityManager.APP_START_MODE_DELAYED_RIGID;

看到这一句,我们去查一下APP_START_MODE_DELAYED_RIGID这个值等于2,而错误log里面startMode正是等于2,这也再一次证实我们的推断是正确的。所以问题的关键就在于uidRec.idle为什么会是true,

 

 

  1. final void idleUids() {
  2. synchronized (this) {
  3. final int N = mActiveUids.size();
  4. if (N <= 0) {
  5. return;
  6. }
  7. final long nowElapsed = SystemClock.elapsedRealtime();
  8. final long maxBgTime = nowElapsed - mConstants.BACKGROUND_SETTLE_TIME;
  9. long nextTime = 0;
  10. if (mLocalPowerManager != null) {
  11. mLocalPowerManager.startUidChanges();
  12. }
  13. for (int i=N-1; i>=0; i--) {
  14. final UidRecord uidRec = mActiveUids.valueAt(i);
  15. final long bgTime = uidRec.lastBackgroundTime;
  16. if (bgTime > 0 && !uidRec.idle) {
  17. if (bgTime <= maxBgTime) {
  18. EventLogTags.writeAmUidIdle(uidRec.uid);
  19. android.util.Log.e("gaoshifeng","bgTime = " + bgTime);
  20. android.util.Log.e("gaoshifeng","maxBgTime = " + maxBgTime);
  21. android.util.Log.e("gaoshifeng","AMS 25781");
  22. uidRec.idle = true;
  23. uidRec.setIdle = true;
  24. doStopUidLocked(uidRec.uid, uidRec);
  25. } else {
  26. if (nextTime == 0 || nextTime > bgTime) {
  27. nextTime = bgTime;
  28. }
  29. }
  30. }
  31. }
  32. if (mLocalPowerManager != null) {
  33. mLocalPowerManager.finishUidChanges();
  34. }
  35. if (nextTime > 0) {
  36. mHandler.removeMessages(IDLE_UIDS_MSG);
  37. mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
  38. nextTime + mConstants.BACKGROUND_SETTLE_TIME - nowElapsed);
  39. }
  40. }
  41. }

在ams里面找一下uidRec.idle被赋值的位置发现在idleUids里面,可以看到这里很明显做了一个时间的判断,而且BACKGROUND_SETTLE_TIME正是60s。这里得判断的意思就是系统boot的时间减去60s,lastBackgroundTime这个时间可以理解成按home键的时间,也就是app开始进入background的时间,如果这个时间比系统boot的时间减去60s小或者相等那么就判断这个app进入background的时间已经满了60s或者更久,那就将uidRec.idle 赋值为true; 也就导致了后面的异常

final long maxBgTime = nowElapsed - mConstants.BACKGROUND_SETTLE_TIME

mConstants.BACKGROUND_SETTLE_TIME = DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000;

 

结论

处于前台时,应用可以自由创建和运行前台与后台 Service。 进入后台时,在一个一分钟的时间窗内,应用仍可以创建和使用 Service。 在该时间窗结束后,应用将被视为处于空闲状态。 此时,系统将停止应用的后台 Service

 

解决方案

经过对startService()流程以及log的分析还有google官方文档的阅读我们可以得出以下几个解决方案

 

方案一

可以考虑在

frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java

getAppStartModeLocked()方法里面加个特殊判断

  1. if ("com.example.illa.testff".equals(packageName)) {
  2. return ActivityManager.APP_START_MODE_NORMAL;
  3. }

 

方案二

这是google官方给出的解决方案

使用startForegroundService,此方法会在状态栏显示通知(可以去掉状态栏方法)

Android 8.0 之前,创建前台 Service 的方式通常是先创建一个后台 Service,然后将该 Service 推到前台。 Android 8.0 有一项复杂功能:系统不允许后台应用创建后台 Service 因此,Android 8.0 引入了一种全新的方法,即 startForegroundService(),以在前台启动新 Service

 

方案三

因为这个限制其实是O版本以后才有的,所以targetSdk < 26也可以实现该新特性规避。

 

方案四

StartService的地方加try catch 防止crash。

 

以此文向刘神表达我的respect!

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/106336
推荐阅读
相关标签
  

闽ICP备14008679号