above

  • Android System Revealed (1) -Activity startup process (1)
  • Android System Revealed (1) -Activity startup process (1)

preface

We know that there are two ways to use a Service: start and bind. This article explains the two ways respectively.

The start of the Service

The process for starting a Service is relatively simple and does not change much between releases. The code in this article is captured in 8.1, but it is also applicable in 8-11

ContextImpl call to AMS



We call startService to call ContextWrapper’s startService method

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

    Context mBase;
    @Override
    public ComponentName startService(Intent service) {
        return mBase.startService(service);
    }
Copy the code

The implementation of Context is ContextImpl

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

    @Override
    public ComponentName startService(Intent service) {
        warnIfCallingFromSystemProcess();
        return startServiceCommon(service, false, mUser);
    }
Copy the code
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(); }}Copy the code

According to reveal the Android system (a) – (on) the Activity start process section. We already know ActivityManager getService () in fact, the AMS

AMS inform ActivityThread



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

final ActiveServices mServices; @Override public ComponentName startService(IApplicationThread caller, Intent service, String resolvedType, boolean requireForeground, 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; try { res = mServices.startServiceLocked(caller, service, resolvedType, callingPid, callingUid, requireForeground, callingPackage, userId); } finally { Binder.restoreCallingIdentity(origId); } return res; }}Copy the code

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 {
         ...
 
         ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
         return cmp;
     }
Copy the code
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r, boolean callerFg, boolean addToStarting) throws TransactionTooLargeException { ... String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false); if (error ! = null) { return new ComponentName("!!" , error); }... return r.name; }Copy the code
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg, boolean whileRestarting, Boolean permissionsReviewRequired) throws TransactionTooLargeException if {/ / send the Service parameters (of state Richard armitage pp! = null && r.app.thread ! = null) { sendServiceArgsLocked(r, execInFg, false); return null; }... final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) ! = 0; final String procName = r.processName; String hostingType = "service"; ProcessRecord app; if (! isolated) { app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false); if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app); if (app ! = null && app.thread ! = null) { try { app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats); // StartService 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 { // If this service runs in an isolated process, then each time ... } // Create if (app == null &&! permissionsReviewRequired) { if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags, hostingType, r.name, false, isolated, false)) == null) { String msg = "Unable to launch app " + r.appInfo.packageName + "/" + r.appInfo.uid + " for service " + r.intent.getIntent() + ": process is bad"; Slog.w(TAG, msg); bringDownServiceLocked(r); return msg; } if (isolated) { r.isolatedProc = app; }}... return null; }Copy the code

The bringUpServiceLocked method gets the process of the Service and starts it with realStartServiceLocked if it exists. Otherwise, create the process and start the Service

private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException { ... boolean created = false; try { ... app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE); app.thread.scheduleCreateService(r, r.serviceInfo, mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo), app.repProcState); r.postNotification(); created = true; } catch (DeadObjectException e) { ... } finally { ... }... }Copy the code

App. thread refers to IApplicationThread, which is implemented as the inner class ApplicationThread of ActivityThread. ApplicationThread inherits from iApplicationThread.stub

ActivityThread start the Service



frameworks/base/core/java/android/app/ActivityThread.java#ApplicationThread


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

Copy the code

The CREATE_SERVICE command is then sent to mH, which is received in H’s handleMessage

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

public void handleMessage(Message msg) { if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); switch (msg.what) { 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; }}Copy the code
private void handleCreateService(CreateServiceData data) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. unscheduleGcIdler(); / / package information LoadedApk packageInfo = getPackageInfoNoCheck (data. Info. ApplicationInfo, data.com patInfo); Service service = null; Try {/ / for class loader Java. Lang. This cl = packageInfo. GetClassLoader (); Service = (Service) cl.loadClass(data.info.name).newinstance (); } catch (Exception e) { if (! mInstrumentation.onException(service, e)) { throw new RuntimeException( "Unable to instantiate service " + data.info.name + ": " + e.toString(), e); } } try { if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name); / / create a context ContextImpl context. = ContextImpl createAppContext (this, packageInfo); context.setOuterContext(service); / / create or obtain Application Application app. = packageInfo makeApplication (false, mInstrumentation); / / initialize the Service Service. Attach (context, this, the data. The info. Name, data. The token, app, ActivityManager. GetService ()); Service.oncreate (); mServices.put(data.token, service); Try {/ / notify the AMS ActivityManager. GetService () serviceDoneExecuting (data token, SERVICE_DONE_EXECUTING_ANON, 0, 0). } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } catch (Exception e) { if (! mInstrumentation.onException(service, e)) { throw new RuntimeException( "Unable to create service " + data.info.name + ": " + e.toString(), e); }}}Copy the code

This step does the following:

  • Obtaining package Information
  • Get the class loader
  • Creating a Service Instance
  • Create context
  • Create or get the Application
  • Initialize the Service
  • The callback notifies the developer that the service has been created
  • Notify the AMS

The Service of the binding

ContextImpl call to AMS



We call startService to call ContextWrapper’s startService method

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

    Context mBase;
    @Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        return mBase.bindService(service, conn, flags);
    }
Copy the code

The implementation of Context is ContextImpl

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

    @Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        warnIfCallingFromSystemProcess();
        return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
                Process.myUserHandle());
    }
Copy the code
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler handler, UserHandle user) { IServiceConnection sd; . if (mPackageInfo ! = null) { sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags); } else { throw new RuntimeException("Not supported in system context"); }... validateServiceIntent(service); try { ... int res = ActivityManager.getService().bindService( mMainThread.getApplicationThread(), getActivityToken(), service, service.resolveTypeIfNeeded(getContentResolver()), sd, flags, getOpPackageName(), user.getIdentifier()); if (res < 0) { throw new SecurityException( "Not allowed to bind to service " + service); } return res ! = 0; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); }}Copy the code

AMS inform ActivityThread



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

final ActiveServices mServices; public int bindService(IApplicationThread caller, IBinder token, Intent service, String resolvedType, IServiceConnection connection, int flags, String callingPackage, int userId) throws TransactionTooLargeException { ... synchronized(this) { return mServices.bindServiceLocked(caller, token, service, resolvedType, connection, flags, callingPackage, userId); }}Copy the code

(Android 10 will skip to the bindIsolatedService method first)

public int bindService(IApplicationThread caller, IBinder token, Intent service, String resolvedType, IServiceConnection connection, int flags, String callingPackage, int userId) throws TransactionTooLargeException { return bindIsolatedService(caller, token, service, resolvedType, connection, flags, null, callingPackage, userId); } public int bindIsolatedService(IApplicationThread caller, IBinder token, Intent service, String resolvedType, IServiceConnection connection, int flags, String instanceName, String callingPackage, int userId) throws TransactionTooLargeException { ... synchronized(this) { return mServices.bindServiceLocked(caller, token, service, resolvedType, connection, flags, instanceName, callingPackage, userId); }}Copy the code

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

int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service, String resolvedType, final IServiceConnection connection, int flags, String callingPackage, final int userId) throws TransactionTooLargeException { ... / / record process information final ProcessRecord callerApp = mAm. GetRecordForAppLocked (caller); . // Activity information ActivityRecord Activity = null; if (token ! = null) { activity = ActivityRecord.isInStackLocked(token); if (activity == null) { Slog.w(TAG, "Binding with unknown activity: " + token); return 0; }}... try { ... / / Service and the relationship between the application AppBindRecord b = s.r etrieveAppBindingLocked (Service, callerApp); ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags, clientLabel, clientIntent); IBinder binder = connection.asBinder(); ArrayList<ConnectionRecord> clist = s.connections.get(binder); . // If BIND_AUTO_CREATE is set, the bringUpServiceLocked method is started. = 0) { s.lastActivity = SystemClock.uptimeMillis(); if (bringUpServiceLocked(s, service.getFlags(), callerFg, false, permissionsReviewRequired) ! = null) { return 0; }}... if (s.app ! = null && b.intent.received) { try { c.conn.connected(s.name, b.intent.binder, false); } catch (Exception e) { ... } if (b.intent.apps.size() == 1 && b.intent.doRebind) { requestServiceBindingLocked(s, b.intent, callerFg, true); } } else if (! b.intent.requested) { requestServiceBindingLocked(s, b.intent, callerFg, false); } getServiceMapLocked(s.userId).ensureNotStartingBackgroundLocked(s); } finally { Binder.restoreCallingIdentity(origId); } return 1; }Copy the code

BringUpServiceLocked (BIND_AUTO_CREATE); bringUpServiceLocked (); bringUpServiceLocked (); bringUpServiceLocked (); Behind the back reference Service startup) call requestServiceBindingLocked method

private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i, boolean execInFg, boolean rebind) throws TransactionTooLargeException { ... if ((! i.requested || rebind) && i.apps.size() > 0) { try { bumpServiceExecutingLocked(r, execInFg, "bind"); r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE); r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind, r.app.repProcState); if (! rebind) { i.requested = true; } i.hasBound = true; i.doRebind = false; } catch (TransactionTooLargeException e) { .. } catch (RemoteException e) { ... } } return true; }Copy the code

App. thread refers to IApplicationThread, which is implemented as the inner class ApplicationThread of ActivityThread. ApplicationThread inherits from iApplicationThread.stub

ActivityThread binding Service



frameworks/base/core/java/android/app/ActivityThread.java#ApplicationThread


        public final void scheduleBindService(IBinder token, Intent intent,
                boolean rebind, int processState) {
            updateProcessState(processState, false);
            BindServiceData s = new BindServiceData();
            s.token = token;
            s.intent = intent;
            s.rebind = rebind;

            if (DEBUG_SERVICE)
                Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="
                        + Binder.getCallingUid() + " pid=" + Binder.getCallingPid());
            sendMessage(H.BIND_SERVICE, s);
        }

Copy the code

It then sends the BIND_SERVICE command to the mH, which is received in H’s handleMessage

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

public void handleMessage(Message msg) { if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); switch (msg.what) { case BIND_SERVICE: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind"); handleBindService((BindServiceData)msg.obj); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } break; }}Copy the code

    final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();
    private void handleBindService(BindServiceData data) {
        Service s = mServices.get(data.token);
        if (DEBUG_SERVICE)
            Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);
        if (s != null) {
            try {
                data.intent.setExtrasClassLoader(s.getClassLoader());
                data.intent.prepareToEnterProcess();
                try {
                    if (!data.rebind) {
                        IBinder binder = s.onBind(data.intent);
                        ActivityManager.getService().publishService(
                                data.token, data.intent, binder);
                    } else {
                        s.onRebind(data.intent);
                        ActivityManager.getService().serviceDoneExecuting(
                                data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                    }
                    ensureJitEnabled();
                } catch (RemoteException ex) {
                    throw ex.rethrowFromSystemServer();
                }
            } catch (Exception e) {
                if (!mInstrumentation.onException(s, e)) {
                    throw new RuntimeException(
                            "Unable to bind to service " + s
                            + " with " + data.intent + ": " + e.toString(), e);
                }
            }
        }
    }
Copy the code

MService is a Map that stores the relationship between token and Server. It obtains the Service to bind from it and then determines whether the Service is bound for the first time. If so, it calls onBind method and AMS publishService method. Otherwise, call onRebind and call AMS’s serviceDoneExecuting method

Let’s look at the publishService method first



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

public void publishService(IBinder token, Intent intent, IBinder service) { // Refuse possible leaked file descriptors if (intent ! = null && intent.hasFileDescriptors() == true) { throw new IllegalArgumentException("File descriptors passed in Intent"); } synchronized(this) { if (! (token instanceof ServiceRecord)) { throw new IllegalArgumentException("Invalid service token"); } mServices.publishServiceLocked((ServiceRecord)token, intent, service); }}Copy the code

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

void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) { final long origId = Binder.clearCallingIdentity(); try { ... if (r ! = null) { Intent.FilterComparison filter = new Intent.FilterComparison(intent); IntentBindRecord b = r.bindings.get(filter); if (b ! = null && ! b.received) { b.binder = service; b.requested = true; b.received = true; for (int conni=r.connections.size()-1; conni>=0; conni--) { ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni); for (int i=0; i<clist.size(); i++) { ConnectionRecord c = clist.get(i); . try { c.conn.connected(r.name, service, false); } catch (Exception e) { ... }}}}... serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false); } } finally { Binder.restoreCallingIdentity(origId); }}Copy the code

Behind it is go serviceDoneExecutingLocked method, we continue to see Arthur c. onn. Connected

frameworks/base/services/core/java/com/android/server/am/ConnectionRecord.java

    final IServiceConnection conn;  // The client connection.
Copy the code

IServiceConnection is an AIDL interface, Concrete implementation for ServiceDispatcher InnerConnection, including ServiceDispatcher is LoadedApk inner classes frameworks/base/core/java/android/app/LoadedApk.java

static final class ServiceDispatcher { ... private static class InnerConnection extends IServiceConnection.Stub { final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher; InnerConnection(LoadedApk.ServiceDispatcher sd) { mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd); } public void connected(ComponentName name, IBinder service, boolean dead) throws RemoteException { LoadedApk.ServiceDispatcher sd = mDispatcher.get(); if (sd ! = null) { sd.connected(name, service, dead); }}}}Copy the code

Then look at the ServiceDispatcher connected method of frameworks/base/core/Java/android/app/LoadedApk java# ServiceDispatcher

public void connected(ComponentName name, IBinder service, boolean dead) {
    if (mActivityThread != null) {
        mActivityThread.post(new RunConnection(name, service, 0, dead));
    } else {
        doConnected(name, service, dead);
    }
}
Copy the code

This method posts a Runnable to the ActivityThread

private final class RunConnection implements Runnable {
    RunConnection(ComponentName name, IBinder service, int command, boolean dead) {
        mName = name;
        mService = service;
        mCommand = command;
        mDead = dead;
    }

    public void run() {
        if (mCommand == 0) {
            doConnected(mName, mService, mDead);
        } else if (mCommand == 1) {
            doDeath(mName, mService);
        }
    }

    final ComponentName mName;
    final IBinder mService;
    final int mCommand;
    final boolean mDead;
}
Copy the code

Inside Runnable is the code for connecting and closing connections.

public void doConnected(ComponentName name, IBinder service, boolean dead) { ServiceDispatcher.ConnectionInfo old; ServiceDispatcher.ConnectionInfo info; synchronized (this) { if (mForgotten) { // We unbound before receiving the connection; ignore // any connection received. return; } old = mActiveConnections.get(name); if (old ! = null && old.binder == service) { // Huh, already have this one. Oh well! return; } if (service ! = null) { // A new service is being connected... set it all up. info = new ConnectionInfo(); info.binder = service; info.deathMonitor = new DeathMonitor(name, service); try { service.linkToDeath(info.deathMonitor, 0); mActiveConnections.put(name, info); } catch (RemoteException e) { // This service was dead before we got it... just // don't do anything with it. mActiveConnections.remove(name); return; } } else { // The named service is being disconnected... clean up. mActiveConnections.remove(name); } if (old ! = null) { old.binder.unlinkToDeath(old.deathMonitor, 0); } } // If there was an old service, it is now disconnected. if (old ! = null) { mConnection.onServiceDisconnected(name); } if (dead) { mConnection.onBindingDied(name); } // If there is a new service, it is now connected. if (service ! = null) { mConnection.onServiceConnected(name, service); }}Copy the code

When binding success callback mConnection. OnServiceConnected (name, service) method

conclusion

The start of the Service

  • The Context to inform AMS
  • AMS inform ActivityThread
  • ActivityThread start the Service

ContextImpl call to AMS

AMS inform ActivityThread

ActivityThread start the Service

The Service of the binding

The binding process is complicated, and it is difficult to describe the simple sequence diagram, so a flow chart is added

  • The Context to inform AMS
  • AMS inform ActivityThread
  • If BIND_AUTO_CREATE is set, the service is directly started; otherwise, the binding continues
  • ActivityThread binding Service
  • If the binding for the first time, call the AMS publishService method, connection Service, and callback onBind, or direct call AMS and callback onReBind serviceDoneExecutingLocked method

ContextImpl call to AMS

AMS inform ActivityThread

ActivityThread binding Service



reference

[1] LIU Wangshu. Advanced Decryption for Android [M]: Publishing House of Electronics Industry,2018-10