首页 » 漏洞 » android 中 Service 的启动过程(上)

android 中 Service 的启动过程(上)

 
文章目录

Service 的启动过程(上)

作为四大组件的 Service ,由于没有 UI 界面,只能默默无闻地在后台工作。虽然我们说他是后台工作的,但是他还是默认进程的主线程中运行的,除非我们给它指定了单独的进程。 Service 的启动过程有两种,一种是 startActivityForResult() ,另一种是 bindService() 。我会在接下来的两篇文章中分别来介绍着两种启动方式,首先我们来探究 startService() 启动服务的过程。

通过 startService 启动一个 Service

ActivityService 都是从 ContextWrapper 继承而来,而 ContextWrapper 实现了 Context 接口,所以我们通常把 ActivityService 看作是 Context 对象。 ContextWrapper 的实现用的是典型的装饰者模式,它的一系列的方法都靠内部的被装饰者的同名方法来实现,这和代理模式有点类似。而这个被装饰者通常是一个 ContextImpl ,它是 Context 接口真正的实现者。

startService()Context 接口定义的一个方法,由于 ActivityService 属于 ContextWrapper 类型,所以这个方法真正的实现在 ContextImpl#startService() 中:

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

startService() 会调用 startServiceCommon() 方法:

private ComponentName startServiceCommon(Intent service, UserHandle user) {     try {         ......         ComponentName cn = ActivityManagerNative.getDefault().startService(             mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(                         getContentResolver()), getOpPackageName(), user.getIdentifier());            ......         return cn;     } catch (RemoteException e) {         throw e.rethrowFromSystemServer();     } }

ActivityManagerNative.getDefault() 返回的是一个 ActivityManagerProxy 对象,它作为 AMS 在本进程的代理,如果我们的应用程序要和 AMS 打交道,必须要以它为媒介。这样看来 ActvitiyManagerService 并不像它名字所暗示的那样只管理 ActivityServcie 也同样归它管。显然,这是一个远程调用,具体的实现在 ActivityManagerService#startService() 中:

@Override public ComponentName startService(IApplicationThread caller, Intent service,         String resolvedType, String callingPackage, int userId)         throws TransactionTooLargeException {     ......     synchronized(this) {         final int callingPid = Binder.getCallingPid();         final int callingUid = Binder.getCallingUid();         final long origId = Binder.clearCallingIdentity();         ComponentName res = mServices.startServiceLocked(caller, service,                 resolvedType, callingPid, callingUid, callingPackage, userId);         Binder.restoreCallingIdentity(origId);         return res;     } }

这个方法并没有做什么,它把事情交给了 mServicemService 是一个 ActiveServices 对象。在早期的安卓版本中并没有这个类,后来重构时抽出这个类专门用来管理 ServiceActiveServices#startServiceLocked() 有点长,我们挑重要部分来看:

ServiceLookupResult res =         retrieveServiceLocked(service, resolvedType, callingPackage,                 callingPid, callingUid, userId, true, callerFg, false); ...... ServiceRecord r = res.record; ...... return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);

这个方法根据这个 Service 对应的 Intent 解析出一个 ServiceRecord ,然后把事情交给了 startServiceInnerLocked() ,这个方法的核心就是调用了 bringUpServiceLocked() ,因此我们进入 ActiveService#bringUpServiceLocked() 看看:

private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,         boolean whileRestarting, boolean permissionsReviewRequired)         throws TransactionTooLargeException {                  ......                  final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;         final String procName = r.processName;         ProcessRecord app;         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);                     realStartServiceLocked(r, app, execInFg);                     return null;                 } catch (TransactionTooLargeException e) {                     throw e;                 } catch (RemoteException e) {                     Slog.w(TAG, "Exception when starting service " + r.shortName, e);                 }              }         } else {             app = r.isolatedProc;         }                  ......                  if (app == null && !permissionsReviewRequired) {             if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,                     "service", r.name, false, isolated, false)) == null) {                 ......                 bringDownServiceLocked(r);                 return msg;             }             if (isolated) {                 r.isolatedProc = app;             }         }                if (!mPendingServices.contains(r)) {             mPendingServices.add(r);         }         ......  }

这段代码中有一个分支:

  • 如果 Service 要运行在启动服务的进程中(默认情况),就直接转到 realStartServiceLocked() 方法;
  • 如果 Service 需要运行在指定进程(注册时给 Service 指定进程名)中就会通知 AMS 新开一个进程(如果这个进程不存在的话),并把这个 Service 对应的 ServiceRecord 对象放进等待列表 mPendingServices 中,当进程开启成功后会从列表中取出 ServiceRecord 对象,然后把 ServiceRecord 对应的 Service 在该进程中创建并运行。

下面我们分别对这两种情况进行研究。

在启动服务的进程中创建 Service

前面说了,如果在启动服务的进程中启动 Service 会进入到 ActiveService#realStartServiceLocked() 方法中,它有两行代码值得关注:

第一行代码

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

关于这行代码说明两点:

  • app 是要运行 Service 的进程对应的 ProcessRecord 对象,系统源码很多地方用 ProcessRecord 代表一个应用进程,这和用 ActivityRecord 代表一个 Activity 以及用 ServiceRecord 代表一个 Service 是同样的道理。
  • thread 是一个 ApplicationThreadProxy 对象,它是应用进程的 ApplicatonThread 对象在 AMS 端的代理,AMS 靠它来和应用进程进行通信。你可能会感到奇怪,前面不是说了应用进程和 AMS 通信靠的是 ActivityManagerProxy 吗,这里怎么出来了一个 ApplicationThreadProxy ?我们要清楚, Binder 实现的进程间通信是单向的,其信息传递方向是 BinderProxy —> Binder 。但是很显然,应用进程和 AMS 是需要双向通信的,所以要想实现双向通信,必须有两对 Binder - BinderProxy 才行,这就是 ApplicationThreadApplicationThreadProxy 存在的的原因。应用进程和 AMS 的通信可以用下图来表示:

android 中 Service 的启动过程(上)

虽然在这里是 ActiveServices 与应用进程通信,但 ActiveServices 也是用来辅助 AMS 管理 Service 的,所以也可以把这个过程看作是 AMS 与 应用进程的通信。(要知道早期的安卓版本没有 ActiveServices 这个类,这些逻辑都是在 AMS 中的)

这行代码的作用是通知应用进程根据已知信息创建一个 Service ,那么应用进程是怎样创建这个 Service 的呢?进入到 ApplicationThread#scheduleCreateService()

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); }

scheduleCreateService() 中,先是把 AMS 传来的信息封装成一个 CreateServcieData 对象,然后调用 sendMessage() 把信息发送出去。注意, sendMessage()ActivitytThead 的方法,因为 ApplicationThreadActivityThread 的内部类,所以对 ActivityThread 有完全的访问权限。这样一来消息就从 ApplicationThread 传到了 ActivityThread ,我们来看看 ActivityThread#sendMessage()

private void sendMessage(int what, Object obj) {     sendMessage(what, obj, 0, 0, false); }

转到另一个重载方法:

private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {     Message msg = Message.obtain();     msg.what = what;     msg.obj = obj;     msg.arg1 = arg1;     msg.arg2 = arg2;     if (async) {         msg.setAsynchronous(true);     }     mH.sendMessage(msg); }

这里把信息传给了 mH 这个 Handler 对象,这个 Handler 对象是在应用进程的主线程中创建的,所以最终的结果是把创建 Service 的消息传到了主线程。现在你终于明白了为什么 Service 会在主线程运行吧?看看 mH 是怎样处理这个消息的:

case CREATE_SERVICE:     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));     handleCreateService((CreateServiceData)msg.obj);     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);     break;

百转千回,消息最终传到了 handlerCreateService()

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 {         if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);         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,                 ActivityManagerNative.getDefault());         service.onCreate();         mServices.put(data.token, service);         try {             ActivityManagerNative.getDefault().serviceDoneExecuting(                     data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);         } catch (RemoteException e) {             throw e.rethrowFromSystemServer();         }     } catch (Exception e) {         ......     } }

这里是 Service 真正被创建的地方,这个方法做了以下几件事:

  • 根据类名用应用程序的类加载器加载并实例化一个 Service
  • 创建一个 ContextImpl 对象,把 Service 作为它的额外层 Context 对象。
  • 为这个 Service 创建一个 Application 对象,当然,如果应用进程已经存在 Application 就不会重复创建,具体大家可以看看 LoadedAPK#makeApplication() 的实现,这里就不详细讲了。
  • Serviceattach() 方法把相应信息附加到 Service ,其中, context 是之前创建的 ContextImpl 对象,在 attach() 方法中会把它作为 Service 内部的被装饰对象; this 代表本进程中的 ActivityThread 对象; data.info.name 是这个 Service 的名称; data.token 是从 AMS 传过来的 ServiceRecord 对象,它是一个 Binder ,作为这个 Service 的标识。
  • 调用 ServiceonCreate() 方法。
  • tokenService 的映射关系保存在 mServices 中。
  • 通过 ActivityManagerProxy 告知 AMS Service 已经创建好了,让其完成后续的工作。

关于后续 AMS 做了哪些事,我们就不深究了,大家有兴趣可以自行阅读源码。现在我们看看 ActiveService#realStartServiceLocked() 中的另一行代码。

第二行代码

sendServiceArgsLocked(r, execInFg, true);

这个方法的核心部分在这里:

r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);

这里又通过 ApplicationThreadProxy 和应用进程进行了通信,我们看看应用进程是怎样响应这个方法调用的:

public final void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,     int flags ,Intent args) {     ServiceArgsData s = new ServiceArgsData();     s.token = token;     s.taskRemoved = taskRemoved;     s.startId = startId;     s.flags = flags;     s.args = args;     sendMessage(H.SERVICE_ARGS, s); }

和前面一样,同样是把信息封装好后通过安卓的消息机制投递到主线程中,我们看看 Handler:mH 是怎样处理这个消息的:

case SERVICE_ARGS:     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceStart: " + String.valueOf(msg.obj)));     handleServiceArgs((ServiceArgsData)msg.obj);     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);     break;

转入 ActivityThread#handleServiceArgs

private void handleServiceArgs(ServiceArgsData data) {     Service s = mServices.get(data.token);     if (s != null) {         try {             ......             int res;             if (!data.taskRemoved) {                 res = s.onStartCommand(data.args, data.flags, data.startId);             } else {                 s.onTaskRemoved(data.args);                 res = Service.START_TASK_REMOVED_COMPLETE;             }             QueuedWork.waitToFinish();             try {                 ActivityManagerNative.getDefault().serviceDoneExecuting(                         data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);             } catch (RemoteException e) {                 throw e.rethrowFromSystemServer();             }             ensureJitEnabled();         } catch (Exception e) {             ......         }     } }

这里先根据 token 取出保存的 Service ,然后根据 data.taskRemoved 的值回调 Service 中的相应方法,一般情况下这个值是为 false 的,也就是说 ServiceonStartCommand() 方法会得到调用。至此在服务启动者的进程中创建服务的过程就分析完了,现在我们看看指定进程中启动服务的过程是怎样的。

在指定进程中创建并启动 Service 的过程

如果注册 Service 的时候我们给 Service 指定了进程名,那么 Service 就会在那个进程中被创建并运行,这个时候启动过程就会走向另一条分支,还记得出现分支的地方吗?它在 ActiveService#bringUpServiceLocked() 中,这条分支会执行以下代码:

private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,         boolean whileRestarting, boolean permissionsReviewRequired)         throws TransactionTooLargeException {         ......                 if (app == null && !permissionsReviewRequired) {             if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,                     "service", r.name, false, isolated, false)) == null) {                 ......                 bringDownServiceLocked(r);                 return msg;             }             if (isolated) {                 r.isolatedProc = app;             }         }                if (!mPendingServices.contains(r)) {             mPendingServices.add(r);         }         ...... }

注意到这行代码 mAm.startProcessLocked()mAm 就是 AMS,这里通过 AMS 开启了一个进程。现在我们进入 ActivityManagerService#startProcessLocked() 这个方法:

private final void startProcessLocked(ProcessRecord app,         String hostingType, String hostingNameStr) {     startProcessLocked(app, hostingType, hostingNameStr, null /* abiOverride */,             null /* entryPoint */, null /* entryPointArgs */); }

转向它的一个重载方法,这个重载方法有点长,但它的核心代码也就一句话:

Process.ProcessStartResult startResult = Process.start(entryPoint,         app.processName, uid, uid, gids, debugFlags, mountExternal,         app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,         app.info.dataDir, entryPointArgs);

Process#start() 会利用这些参数向 native 层 fork 一个进程。注意到这个方法的第一参数 String:entryPoint ,顾名思义,它是进程的入口点,其值为 "android.app.ActivityThread" 。对于 ActivityThread 我们应该很熟悉了,每个应用进程都有一个 ActivityThread 对象,它代表着应用进程的主线程,处理着内部类 ApplicationThread 发送过来的来自 AMS 的各种消息。进程创建好后 native 层代码后就会调用这个类的静态方法 main() ,它的实现如下:

public static void main(String[] args) {     ......     final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());     TrustedCertificateStore.setDefaultUserDirectory(configDir);     Process.setArgV0("<pre-initialized>");     Looper.prepareMainLooper();     ActivityThread thread = new ActivityThread();     thread.attach(false);     if (sMainThreadHandler == null) {         sMainThreadHandler = thread.getHandler();     }     if (false) {         Looper.myLooper().setMessageLogging(new                 LogPrinter(Log.DEBUG, "ActivityThread"));     }     // End of event ActivityThreadMain.     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);     Looper.loop();     throw new RuntimeException("Main thread loop unexpectedly exited"); }

它很简短,主要做了这几件事:

  • 为主线程准备消息循环
  • 为应用进程创建一个 ActivityThread 对象
  • 调用 ActivityThreadattach() 方法
  • 开启消息循环

这样我们的应用进程就进入到一个死循环中了,不断的接受消息并执行。所以我们会说安卓应用是消息驱动的。我们重点关注 attach() 方法,它有这样一段代码值得关注:

final IActivityManager mgr = ActivityManagerNative.getDefault(); try {     mgr.attachApplication(mAppThread); } catch (RemoteException ex) {     throw ex.rethrowFromSystemServer(); }

这里通过 ActivityManagerProxy 远程调用了 AMS 的 attachApplication() 方法,参数是 mAppThread ,而它是这样初始化的:

final ApplicationThread mAppThread = new ApplicationThread();

说明它就是 ApplicationThread 对象,前面说了,AMS 要想向应用进程发送消息,需要借助 ApplicationThreadProxy 对象。而通过 Binder 机制 ApplicationThread 在 AMS 那边就转化成了 ApplicationThreadProxy 对象,所以这个对象就是在此时传给 AMS 的。在 AMS 中, attachApplication() 会直接调用 attachApplicationLocked() ,对于这个方法,我们挑需要的代码段来看:

if (!badApp) {     try {         didSomething |= mServices.attachApplicationLocked(app, processName);     } catch (Exception e) {         Slog.wtf(TAG, "Exception thrown starting services in " + app, e);         badApp = true;     } }

mServices 就是 ActiveServices ,进入 ActiveServices#attachApplicationLocked()

boolean attachApplicationLocked(ProcessRecord proc, String processName)         throws RemoteException {     boolean didSomething = false;     // Collect any services that are waiting for this process to come up.     if (mPendingServices.size() > 0) {         ServiceRecord sr = null;         try {             for (int i=0; i<mPendingServices.size(); i++) {                 sr = mPendingServices.get(i);                 if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid                         || !processName.equals(sr.processName))) {                     continue;                 }                 mPendingServices.remove(i);                 i--;                 proc.addPackage(sr.appInfo.packageName, sr.appInfo.versionCode,                         mAm.mProcessStats);                 realStartServiceLocked(sr, proc, sr.createdFromFg);                 didSomething = true;                 if (!isServiceNeeded(sr, false, false)) {                     bringDownServiceLocked(sr);                 }             }         } catch (RemoteException e) {            ......         }     }     ...... }

大家还记得 mPendingServices 吗?前面说了,如果在指定进程当中运行一个 Service ,会先创建一个进程,然后在把该 Service 对应的 ServiceRecord 对象放入 mPendingServices 中,待进程创建好了就会从中取出 ServiceRecord ,然后根据它在进程中创建 Service 。具体过程是怎样的呢?我们看看这个方法主要作了哪些事:

遍历 mPendingServices ,根据 ProcessRecord:procString:processName 提供的进程信息找出要运行在这个进程的 ServiceRecord ,然后调用 realStartServiceLocked() 方法并把找到的 ServiceRecord 作为参数传入其中。 realStartServiceLocked() 之后的流程前面已经有介绍,这里就不重复讲了。

至此,通过 startService() 启动服务的整个过程就介绍完了,在接下来的一篇文章中我会介绍通过 bindService() 创建并绑定一个 Service 的详细流程。感谢大家的阅读,有什么不对的地方还望大家不吝赐教。

原文链接:android 中 Service 的启动过程(上),转载请注明来源!

0