流水账的形式解决startForegroundService与startService的兼容性问题

1,305 阅读6分钟

源码地址

ContextImpl.java

ActivityManagerService.java

ActivityThread.java

ActiveServices.java

既然startForegroundService是在Android8.0之后出现的问题, 就以Android8.0源码进行分析, startForegroundService为何有兼容性问题, 以及该如何解决, 采用流水式阅读代码进行分析.

1. startForegroundService
class ContextImpl::
@Override
public ComponentName startForegroundService(Intent service) {
    warnIfCallingFromSystemProcess();
    return startServiceCommon(service, true, mUser);
}

@Override
public ComponentName startService(Intent service) {
    warnIfCallingFromSystemProcess();
    return startServiceCommon(service, false, mUser);
}

startForegroundService()调用到startServiceCommon时, 第二个参数为true, startService时第二个参数传入false.

2. startServiceCommon
class ContextImpl::
private ComponentName startServiceCommon(Intent service, boolean requireForeground, UserHandle user) {
    try {
        validateServiceIntent(service);
        service.prepareToLeaveProcess(this);
      	// ActivityManagerService = ActivityManager.getService();
        ComponentName cn = ActivityManager.getService().startService(
                  mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                              getContentResolver()), requireForeground,
                              getOpPackageName(), user.getIdentifier());
        //...
        return cn;
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

ActivityManager.getService()返回AMS对象

3. AMS.startService
class ActivityManagerService::
@Override
public ComponentName startService(IApplicationThread caller, Intent service,
              String resolvedType, boolean requireForeground, String callingPackage, int userId) {
    enforceNotIsolatedCaller("startService");
    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;
    }
}

抓住主线:requireForeground

4. startServiceLocked
class ActiveServices::
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
              int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId) {  
    final boolean callerFg;
    if (caller != null) {
        final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
        callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
    } else {
        callerFg = true;
    } 
    ServiceLookupResult res = retrieveServiceLocked(service, resolvedType, callingPackage,
                      callingPid, callingUid, userId, true, callerFg, false);
    ServiceRecord r = res.record;  
    r.lastActivity = SystemClock.uptimeMillis();
    r.startRequested = true;
    r.delayedStop = false;
    /**
     * startForegroundService(): fgRequired = true;
     * startService(): fgRequired = false;
     */
    r.fgRequired = fgRequired;
    r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
                  service, neededGrants, callingUid));
    final ServiceMap smap = getServiceMapLocked(r.userId);
    boolean addToStarting = false;
    // startForegroundService()的情况下fgRequired = true, 跳过if, addToStarting = false;
    if (!callerFg && !fgRequired && r.app == null
                  && mAm.mUserController.hasStartedUserState(r.userId)) {
				ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
        if (proc == null || proc.curProcState > ActivityManager.PROCESS_STATE_RECEIVER) {
            //...
            addToStarting = true;
        } else if (proc.curProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
            // ...
            addToStarting = true;
        } 
    } 
    ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
    return cmp;
}

1、先考虑startForegroundService()的情况下, fgRequired = true, 跳过if语句.

5. startServiceInnerLocked
class ActiveServices::
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
              boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
    ServiceState stracker = r.getTracker();
    if (stracker != null) {
        stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
    }
    r.callStart = false;
    synchronized (r.stats.getBatteryStats()) {
        r.stats.startRunningLocked();
    }
    // 1.启动service
    String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
    if (error != null) {
        return new ComponentName("!!", error);
    }  
    // 2.startForegroundService: addToStarting = false;
    // 3.startService: addToStarting = true.
    if (r.startRequested && addToStarting) {
        boolean first = smap.mStartingBackground.size() == 0;
        smap.mStartingBackground.add(r);
        r.startingBgTimeout = SystemClock.uptimeMillis() + mAm.mConstants.BG_START_TIMEOUT;
        if (first) {
            smap.rescheduleDelayedStartsLocked();
        }
    } else if (callerFg || r.fgRequired) {
        // 4.startForegroundService: fgRequired = true;
        // 5.startService: fgRequired = false.
        smap.ensureNotStartingBackgroundLocked(r);
    }  
    return r.name;
}
6. bringUpServiceLocked
class ActiveServices::
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
              boolean whileRestarting, boolean permissionsReviewRequired) {  
    if (r.app != null && r.app.thread != null) {
        sendServiceArgsLocked(r, execInFg, false);
        return null;
    }
    if (!whileRestarting && mRestartingServices.contains(r)) {
        // If waiting for a restart, then do nothing.
        return null;
    }
    if (mRestartingServices.remove(r)) {
        clearRestartingIfNeededLocked(r);
    }
    // Make sure this service is no longer considered delayed, we are starting it now.
    if (r.delayed) {
        getServiceMapLocked(r.userId).mDelayedStartList.remove(r);
        r.delayed = false;
    }
    // Make sure that the user who owns this service is started.  If not,
    // we don't want to allow it to run.
    if (!mAm.mUserController.hasStartedUserState(r.userId)) {
2093              bringDownServiceLocked(r);
2094              return msg;
2095          }
    final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
2108          final String procName = r.processName;
2109          String hostingType = "service";
          ProcessRecord app;
2111  
    if (!isolated) {
        app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
        if (app != null && app.thread != null) {
            try {
                app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
                // 启动Service
                realStartServiceLocked(r, app, execInFg);
                return null;
            } catch (TransactionTooLargeException e) {
                throw e;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting service " + r.shortName, e);
            }
            // If a dead object exception was thrown -- fall through to
            // restart the application.
        }
    } else {
        app = r.isolatedProc;
        if (WebViewZygote.isMultiprocessEnabled()
                      && r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) {
            hostingType = "webview_service";
        }
    }
    // Not running -- get it started, and enqueue this service record
    // to be executed when the app comes up.
    if (app == null && !permissionsReviewRequired) {
        if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                      hostingType, r.name, false, isolated, false)) == null) {
            bringDownServiceLocked(r);
            return msg;
        }
        if (isolated) {
            r.isolatedProc = app;
        }
    }
    if (!mPendingServices.contains(r)) {
        mPendingServices.add(r);
    }  
    if (r.delayedStop) {
        // Oh and hey we've already been asked to stop!
        r.delayedStop = false;
        if (r.startRequested) {
            stopServiceLocked(r);
        }
    }  
    return null;
}
7. realStartServiceLocked
class ActiveServices::
private final void realStartServiceLocked(ServiceRecord r,
2190              ProcessRecord app, boolean execInFg) throws RemoteException {
2197          r.app = app;
2198          r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
2199  
2200          final boolean newService = app.services.add(r);
2201          bumpServiceExecutingLocked(r, execInFg, "create");
2202          mAm.updateLruProcessLocked(app, false, null);
2203          updateServiceForegroundLocked(r.app, /* oomAdj= */ false);
2204          mAm.updateOomAdjLocked();
2205  
2206          boolean created = false;
    try {
        app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
        // 1.启动目标Service
        app.thread.scheduleCreateService(r, r.serviceInfo,
                      mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                      app.repProcState);
        r.postNotification();
        created = true;
    } finally {
    }
    // 2.发送延迟消息
    sendServiceArgsLocked(r, execInFg, true);
}
8. scheduleCreateService
class ActivityThread::
public final void scheduleCreateService(IBinder token,
                  ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
    updateProcessState(processState, false);
    CreateServiceData s = new CreateServiceData();
    s.token = token;
    s.info = info;
    s.compatInfo = compatInfo;
    sendMessage(H.CREATE_SERVICE, s);
}
9. CREATE_SERVICE
private class H extends Handler {
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case CREATE_SERVICE:
                handleCreateService((CreateServiceData)msg.obj);
            break;
        }
    }
}
10. handleCreateService
private void handleCreateService(CreateServiceData data) {
    LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo, data.compatInfo);
    Service service = null;
    try {
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        service = (Service) cl.loadClass(data.info.name).newInstance();
    } catch (Exception e) {
    }
    try {
        ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
        context.setOuterContext(service);
        Application app = packageInfo.makeApplication(false, mInstrumentation);
        service.attach(context, this, data.info.name, data.token, app,
                      ActivityManager.getService());
        // 启动Service
        service.onCreate();
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}
11. serviceDoneExecuting
class AMS::
public void serviceDoneExecuting(IBinder token, int type, int startId, int res) {
    synchronized(this) {
        mServices.serviceDoneExecutingLocked((ServiceRecord)token, type, startId, res);
    }
}
12. sendServiceArgsLocked发送延迟消息
class ActiveServices::
private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,
              boolean oomAdjusted) throws TransactionTooLargeException {
    final int N = r.pendingStarts.size();
    if (N == 0) {
        return;
    }
    ArrayList<ServiceStartArgs> args = new ArrayList<>();
    while (r.pendingStarts.size() > 0) {
        // 1.startForegroundService: fgRequired = true;
        // 2.startService: fgRequired = false;
        if (r.fgRequired && !r.fgWaiting) {
            // 3.isForeground当前Service是否为前台, true: 是, false: 否
            if (!r.isForeground) {
                // 4.如果当前Service不是前台, 则发送延迟消息
                scheduleServiceForegroundTransitionTimeoutLocked(r);
            } else {
                // 5.如果当前Service为前台, 不作处理, 同时将fgRequired置为true, 也就是不需要
                r.fgRequired = false;
            }
        }
    } 
}
13. scheduleServiceForegroundTransitionTimeoutLocked
class ActiveServices::
void scheduleServiceForegroundTransitionTimeoutLocked(ServiceRecord r) {
    Message msg = mAm.mHandler.obtainMessage(
                  ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG);
    msg.obj = r;
    r.fgWaiting = true;
    // 5秒延迟
    mAm.mHandler.sendMessageDelayed(msg, SERVICE_START_FOREGROUND_TIMEOUT);
}
14. SERVICE_FOREGROUND_TIMEOUT_MSG接收延迟消息
class AMS::
case SERVICE_FOREGROUND_TIMEOUT_MSG: {
    mServices.serviceForegroundTimeout((ServiceRecord)msg.obj);
} break;
15. serviceForegroundTimeout
class ActiveServices::
void serviceForegroundTimeout(ServiceRecord r) {
    ProcessRecord app;
    synchronized (mAm) {
        if (!r.fgRequired || r.destroying) {
        return;
    }
    app = r.app;
    r.fgWaiting = false;
        stopServiceLocked(r);
    }
    if (app != null) {
        // 抛出异常
        mAm.mAppErrors.appNotResponding(app, null, null, false,
                      "Context.startForegroundService() did not then call Service.startForeground()");
    }
}

通过startForegroundService启动Service之后, 会发送一条延迟5秒的消息, 轮询器收到该消息之后, 抛出异常

如何解决?

在onCreate中执行startForeground方法

16. startForeground
class Service::
public final void startForeground(int id, Notification notification) {
    try {
        mActivityManager.setServiceForeground(
                    new ComponentName(this, mClassName), mToken, id,
                    notification, 0);
    } catch (RemoteException ex) {
    }
}
16. setServiceForeground
class AMS::
@Override
public void setServiceForeground(ComponentName className, IBinder token,
18218              int id, Notification notification, int flags) {
18219          synchronized(this) {
18220              mServices.setServiceForegroundLocked(className, token, id, notification, flags);
18221          }
18222      }
17. setServiceForegroundLocked
class ActiveServices::
public void setServiceForegroundLocked(ComponentName className, IBinder token,
730              int id, Notification notification, int flags) {
731          final int userId = UserHandle.getCallingUserId();
732          final long origId = Binder.clearCallingIdentity();
    try {
        // 1.寻找目标Service
        ServiceRecord r = findServiceLocked(className, token, userId);
        if (r != null) {
            // 2.找到目标Service之后, 取消延迟消息的发送
            setServiceForegroundInnerLocked(r, id, notification, flags);
        }
738          } finally {
739              Binder.restoreCallingIdentity(origId);
740          }
741      }

找到目标Servicezhihou, 取消延迟消息的发送.

18. setServiceForegroundInnerLocked
class ActiveServices::
private void setServiceForegroundInnerLocked(ServiceRecord r, int id, Notification notification, int flags) {
    if (id != 0) {
        if (notification == null) {
            throw new IllegalArgumentException("null notification");
        }
        // Instant apps need permission to create foreground services.
        if (r.appInfo.isInstantApp()) {
            final int mode = mAm.mAppOpsService.checkOperation(
1028                          AppOpsManager.OP_INSTANT_APP_START_FOREGROUND,
1029                          r.appInfo.uid,
1030                          r.appInfo.packageName);
1031                  switch (mode) {
1032                      case AppOpsManager.MODE_ALLOWED:
1033                          break;
1034                      case AppOpsManager.MODE_IGNORED:
1035                          Slog.w(TAG, "Instant app " + r.appInfo.packageName
1036                                  + " does not have permission to create foreground services"
1037                                  + ", ignoring.");
1038                          return;
1039                      case AppOpsManager.MODE_ERRORED:
1040                          throw new SecurityException("Instant app " + r.appInfo.packageName
1041                                  + " does not have permission to create foreground services");
1042                      default:
1043                          try {
1044                              if (AppGlobals.getPackageManager().checkPermission(
1045                                      android.Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE,
1046                                      r.appInfo.packageName,
1047                                      r.appInfo.uid) != PackageManager.PERMISSION_GRANTED) {
1048                                  throw new SecurityException("Instant app " + r.appInfo.packageName
1049                                          + " does not have permission to create foreground"
1050                                          + "services");
1051                              }
1052                          } catch (RemoteException e) {
1053                              throw new SecurityException("Failed to check instant app permission." ,
1054                                      e);
1055                          }
1056                  }
1057              }
1058              if (r.fgRequired) {
1062                  r.fgRequired = false;
1063                  r.fgWaiting = false;
                    // 取消延迟消息的发送
1064                  mAm.mHandler.removeMessages(
1065                          ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
1066              }
1067              if (r.foregroundId != id) {
1068                  cancelForegroundNotificationLocked(r);
1069                  r.foregroundId = id;
1070              }
1071              notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
1072              r.foregroundNoti = notification;
1073              if (!r.isForeground) {
1074                  final ServiceMap smap = getServiceMapLocked(r.userId);
1075                  if (smap != null) {
1076                      ActiveForegroundApp active = smap.mActiveForegroundApps.get(r.packageName);
1077                      if (active == null) {
1078                          active = new ActiveForegroundApp();
1079                          active.mPackageName = r.packageName;
1080                          active.mUid = r.appInfo.uid;
1081                          active.mShownWhileScreenOn = mScreenOn;
1082                          if (r.app != null) {
1083                              active.mAppOnTop = active.mShownWhileTop =
1084                                      r.app.uidRecord.curProcState
1085                                              <= ActivityManager.PROCESS_STATE_TOP;
1086                          }
1087                          active.mStartTime = active.mStartVisibleTime
1088                                  = SystemClock.elapsedRealtime();
1089                          smap.mActiveForegroundApps.put(r.packageName, active);
1090                          requestUpdateActiveForegroundAppsLocked(smap, 0);
1091                      }
1092                      active.mNumActive++;
1093                  }
1094                  r.isForeground = true;
1095              }
1096              r.postNotification();
1097              if (r.app != null) {
1098                  updateServiceForegroundLocked(r.app, true);
1099              }
1100              getServiceMapLocked(r.userId).ensureNotStartingBackgroundLocked(r);
1101              mAm.notifyPackageUse(r.serviceInfo.packageName,
1102                                   PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE);
1103          } else {
1104              if (r.isForeground) {
1105                  final ServiceMap smap = getServiceMapLocked(r.userId);
1106                  if (smap != null) {
1107                      decActiveForegroundAppLocked(smap, r);
1108                  }
1109                  r.isForeground = false;
1110                  if (r.app != null) {
1111                      mAm.updateLruProcessLocked(r.app, false, null);
1112                      updateServiceForegroundLocked(r.app, true);
1113                  }
1114              }
1115              if ((flags & Service.STOP_FOREGROUND_REMOVE) != 0) {
1116                  cancelForegroundNotificationLocked(r);
1117                  r.foregroundId = 0;
1118                  r.foregroundNoti = null;
1119              } else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
1120                  r.stripForegroundServiceFlagFromNotification();
1121                  if ((flags & Service.STOP_FOREGROUND_DETACH) != 0) {
1122                      r.foregroundId = 0;
1123                      r.foregroundNoti = null;
1124                  }
1125              }
1126          }
1127      }