基于版本:Android O

0. 前言

通过source code 分析了AMS 中service 的启动过程,bindService 相对复杂一点,主要是多了一些service 和app的绑定关系处理。本文继续结合source code 来剖析。

1. 入口函数

1.1 bindService()

@Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        warnIfCallingFromSystemProcess();
        return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
                Process.myUserHandle());
    }

    /** @hide */
    @Override
    public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
            UserHandle user) {
        return bindServiceCommon(service, conn, flags, mMainThread.getHandler(), user);
    }

参数:

  • service 的intent
  • bind 时候的ServiceConnection
  • bind service 用到的flag,例如BIND_AUTO_CREATE
  • 当前的user

1.2 bindServiceCommon()

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
            handler, UserHandle user) {
        // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
        IServiceConnection sd;
        if (conn == null) {
            throw new IllegalArgumentException("connection is null");
        }
        if (mPackageInfo != null) {
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
        } else {
            throw new RuntimeException("Not supported in system context");
        }
        validateServiceIntent(service);
        try {
            IBinder token = getActivityToken();
            if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
                    && mPackageInfo.getApplicationInfo().targetSdkVersion
                    < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                flags |= BIND_WAIVE_PRIORITY;
            }
            service.prepareToLeaveProcess(this);
            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();
        }
    }
  • ServiceConnection 不能为null,必须要创建
  • android L 之后的版本不能隐式启动service
  • 调用AMS 的bindService(),calling 的thread,calling 的package 都会传入

注意的是bindService 第 5 个参数sd,传入到AMS 不在是client 的ServiceConnection,而是ServiceDispatcher,详细可以看LoadedApk.ServiceDispatcher,每个应用都有这样的dispatcher 会注册到AMS 中,用于AMS 的回调。

static final class ServiceDispatcher {
        private final ServiceDispatcher.InnerConnection mIServiceConnection;
        private final ServiceConnection mConnection;
        private final Context mContext;
        private final Handler mActivityThread;
        private final ServiceConnectionLeaked mLocation;
        private final int mFlags;

        private RuntimeException mUnbindLocation;

        private boolean mForgotten;

        private static class ConnectionInfo {
            IBinder binder;
            IBinder.DeathRecipient deathMonitor;
        }

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

        ...
        ...

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

        ...
        ...

        public void doConnected(ComponentName name, IBinder service, boolean dead) {
            ServiceDispatcher.ConnectionInfo old;
            ServiceDispatcher.ConnectionInfo info;

            ...
            ...

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

        ...
        ...
    }

最终会调用到应用中定义的ServiceConnection 的onServiceDisconnected() 和 onServiceConnected(), 分析AMS 的时候会继续说明这里用处。

2. bindServiceLocked

通过上面的bindService,最终会调用到ActiveServices 中的bindServiceLocked()

2.1 retrieveServiceLocked()

这个在 Android service 启动篇之 startService 的 3.2 节中已经大概解释了下 ,如果app 已经service 在内部进行运行,那么需要满足下面几个条件:

  • service 需要置上flag ServiceInfo.FLAG_EXTERNAL_SERVICE
  • service 需要置上flag ServiceInfo.FLAG_ISOLATED_PROCESS
  • service 的exported 必须为true
  • app 在bind service 的时候置上flag Context.BIND_EXTERNAL_SERVICE
ComponentName name = new ComponentName(
                sInfo.applicationInfo.packageName, sInfo.name);
        if ((sInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0) {
            if (isBindExternal) {
                if (!sInfo.exported) {
                    throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
                            " is not exported");
                }
                if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0) {
                    throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
                            " is not an isolatedProcess");
                }
                // Run the service under the calling package's application.
                ApplicationInfo aInfo = AppGlobals.getPackageManager().getApplicationInfo(
                        callingPackage, ActivityManagerService.STOCK_PM_FLAGS, userId);
                if (aInfo == null) {
                    throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " +
                            "could not resolve client package " + callingPackage);
                }
                sInfo = new ServiceInfo(sInfo);
                sInfo.applicationInfo = new ApplicationInfo(sInfo.applicationInfo);
                sInfo.applicationInfo.packageName = aInfo.packageName;
                sInfo.applicationInfo.uid = aInfo.uid;
                name = new ComponentName(aInfo.packageName, name.getClassName());
                service.setComponent(name);
            } else {
                throw new SecurityException("BIND_EXTERNAL_SERVICE required for " +
                        name);
            }

最终获取的ComponentName 的package 为app 的package,name 是service 的name。

2.2 变量AppBindRecord b

AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);

来看下函数retrieveAppBindingLocked():

public AppBindRecord retrieveAppBindingLocked(Intent intent,
            ProcessRecord app) {
        Intent.FilterComparison filter = new Intent.FilterComparison(intent);
        IntentBindRecord i = bindings.get(filter);
        if (i == null) {
            i = new IntentBindRecord(this, filter);
            bindings.put(filter, i);
        }
        AppBindRecord a = i.apps.get(app);
        if (a != null) {
            return a;
        }
        a = new AppBindRecord(this, i, app);
        i.apps.put(app, a);
        return a;
    }

 注意,后面在bringUpServiceLocked() 的时候会根据bindings 来确定是否可以bind service。

变量类型为AppBindRecord,如下:

final class AppBindRecord {
    final ServiceRecord service;    // The running service.
    final IntentBindRecord intent;  // The intent we are bound to.
    final ProcessRecord client;     // Who has started/bound the service.

    final ArraySet<ConnectionRecord> connections = new ArraySet<>();
                                    // All ConnectionRecord for this client.

retrieveServiceLocked查找到ServiceRecord之后,生成Service和Client(callerApp)之间的绑定关系AppBindRecord,AppBindRecord的字段包括service,client,intent,确定了他们之间的绑定关系。

2.3 变量 ConnectionRecord c

ConnectionRecord c = new ConnectionRecord(b, activity,
            connection, flags, clientLabel, clientIntent);

其中的connection 是从app 中传进来的,在上面 1.2 节中已经解析过,相当于一个ServiceDispatcher。记住了每一个需要bind 到该service 的所有信息。在bind 信息有更新的时候也会通过这里的conn 进行connected() 调用。

final class ConnectionRecord {
    final AppBindRecord binding;    // The application/service binding.
    final ActivityRecord activity;  // If non-null, the owning activity.
    final IServiceConnection conn;  // The client connection.
    final int flags;                // Binding options.
    final int clientLabel;          // String resource labeling this client.
    final PendingIntent clientIntent; // How to launch the client.
    String stringName;              // Caching of toString.
    boolean serviceDead;            // Well is it?

2.4 变量 connections

IBinder binder = connection.asBinder();
    ArrayList<ConnectionRecord> clist = s.connections.get(binder);
    if (clist == null) {
        clist = new ArrayList<ConnectionRecord>();
        s.connections.put(binder, clist);
    }
    clist.add(c);
    b.connections.add(c);
    if (activity != null) {
        if (activity.connections == null) {
            activity.connections = new HashSet<ConnectionRecord>();
        }
        activity.connections.add(c);
    }
    b.client.connections.add(c);
    if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
        b.client.hasAboveClient = true;
    }
    if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
        s.whitelistManager = true;
    }
    if (s.app != null) {
        updateServiceClientActivitiesLocked(s.app, c, true);
    }
    clist = mServiceConnections.get(binder);
    if (clist == null) {
        clist = new ArrayList<ConnectionRecord>();
        mServiceConnections.put(binder, clist);
    }
    clist.add(c);

AppBindRecord的connections字段则保存了这个client的所有ServiceConnection连接ConnectionRecord,ConnectionRecord和IServiceConnection对象是对应的。ServiceRecord也有个connections列表,但ServiceRecord的connections列表存储的是这个Service相关的所有ConnectionRecord,Service和Client之间是多对多的关系,所以其各自维护了一个connections。

2.5 bringUpServiceLocked()

if ((flags&Context.BIND_AUTO_CREATE) != 0) {
        s.lastActivity = SystemClock.uptimeMillis();
        if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
                permissionsReviewRequired) != null) {
            return 0;
        }
    }

如果bindService 的时候置上flag Context.BIND_AUTO_CREATE,那么会直接进入bringUpServiceLocked() 进行唤醒。

2.5.1 进入create 流程

在 Android service 启动篇之 startService 的 3.6.1

2.5.2 进入bind service 流程

接着会进入bind service 流程:

private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg)
            throws TransactionTooLargeException {
        for (int i=r.bindings.size()-1; i>=0; i--) {
            IntentBindRecord ibr = r.bindings.valueAt(i);
            if (!requestServiceBindingLocked(r, ibr, execInFg, false)) {
                break;
            }
        }
    }

这里的bindings 就是上面 2.2 节中创建的。

注意:

其实在start service 的时候也会进入这个流程,但是bindings 为空,所以,最终不会进入bind service 的流程。 

详细的函数requestServiceBindingsLocked(r, execInFg); 分析请看下面2.6节。

2.5.3 进入start service 流程

其实在bringUpServiceLocked() 最后会通过函数:

sendServiceArgsLocked(r, execInFg, true);

进入start service 的流程,详细可以看 Android service 启动篇之 startService 的 3.6.1.2 节,但是对于bind service 来说,在函数的最开始条件就不满足return 了。详细看Android service 启动篇之 startService 的 3.4 节和3.6.1.1 节。

2.6 requestServiceBindingsLocked()

上一节,我们看到如果service 第一次启动的时候,会进入realStartServiceLocked()。最终会进入该函数,执行bind 的完成流程。

但是,如果service 已经启动,这个时候在bringUpServiceLocked() 中:

if (r.app != null && r.app.thread != null) {
            sendServiceArgsLocked(r, execInFg, false);
            return null;
        }

而 sendServiceArgsLocked() 在上面 2.5.3 中知道bind service 是不会执行的。

那最终会跳过 2.5 节的流程继续往下执行:

if (s.app != null && b.intent.received) {
            // Service is already running, so we can immediately
            // publish the connection.
            try {
                c.conn.connected(s.name, b.intent.binder, false);
            } catch (Exception e) {
                Slog.w(TAG, "Failure sending service " + s.shortName
                        + " to connection " + c.conn.asBinder()
                        + " (in " + c.binding.client.processName + ")", e);
            }

            // If this is the first app connected back to this binding,
            // and the service had previously asked to be told when
            // rebound, then do so.
            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);
        }

第一次bind service 的时候requested 为false,最终会调用到requestServiceBindingLocked():

private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
            boolean execInFg, boolean rebind) throws TransactionTooLargeException {
        if (r.app == null || r.app.thread == null) {
            // If service is not currently running, can't yet bind.
            return false;
        }
        if (DEBUG_SERVICE) Slog.d(TAG_SERVICE, "requestBind " + i + ": requested=" + i.requested
                + " rebind=" + rebind);
        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) {
                // Keep the executeNesting count accurate.
                if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r, e);
                final boolean inDestroying = mDestroyingServices.contains(r);
                serviceDoneExecutingLocked(r, inDestroying, inDestroying);
                throw e;
            } catch (RemoteException e) {
                if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r);
                // Keep the executeNesting count accurate.
                final boolean inDestroying = mDestroyingServices.contains(r);
                serviceDoneExecutingLocked(r, inDestroying, inDestroying);
                return false;
            }
        }
        return true;
    }

通过该函数进行bind 的request 申请,通过函数:

bumpServiceExecutingLocked(r, execInFg, "bind");

埋下onBind操作的ANR 炸弹,要求onBind 处理不能超过20s。

并通过:

r.app.thread.scheduleBindService()

最终调用handleBindService函数:

private void handleBindService(BindServiceData data) {
        Service s = mServices.get(data.token);
        ...
        if (s != null) {
            try {
                ...
                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) {
                ...
            }
        }
    }

onBind 接口调用完成后会调用AMS.publishService,最终会调用到ActiveServices.publishServiceLocked 进行onBind 的后期处理,最终会调用serviceDoneExecutingLocked 解除ANR炸弹

至此,bindService的流程基本完成,总结下:

  • 注意两个核心变量AppBindRecord 和ConnectionRecord;
  • 如果没有启动service,会顺序进入create、bind、start 流程,并且在每个流程都会埋下ANR炸弹,要求每个流程在20s 内处理完成,在处理结束后ActiveServices 会拆除炸弹;
  • 如果已经启动service,会进入onStartCommand流程,同样伴随着ANR炸弹;