为什么要写这篇文章
当初刚接触IPC的时候,网上的资料并没有现在这么丰富,基本都是告诉我们安卓提供了AIDL来帮我们来实现binder跨进程。而AIDL的实现案例,清一色都是通过bindservice来创建一个远程服务,然后通过这个服务提供一个binder,我们的主进程再通过serviceConnnection来获取binderProxy。然后通过asInterface方法把ibinder转成iinterface,再实现远程调用。当初就有一个疑问,ibinder哪来的。当时在网上并没有搜到答案。我相信有好多刚接触IPC的同学也会有同样的疑问,所以就决定从源码的角度来解决这个疑惑。
源码分析
private class Connection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
bindService(new Intent(this, BinderService.class),
new Connection(), Context.BIND_AUTO_CREATE);
这是client的标准写法。
public class BinderService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new XXX.Stub();
}
}
这是server的标准写法,XXX.Stub()是个binder,也是个抽象类,具体方法实现我们自己写。当然BinderService需要设置process属性。
我们的目的就是分析client的onServiceConnected方法返回的service究竟怎么来的。
从bindService方法开始,这个方法是在contextWrapper中
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
return mBase.bindService(service, conn, flags);
}
mBase我们之前说过是一个ContextImpl,是在activity的attach方法中去赋值的。
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
warnIfCallingFromSystemProcess();
return bindServiceCommon(service, conn, flags, mMainThread.getHandler(), getUser());
}
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();
}
}
sd是通过mPackageInfo获取的,mPackageInfo是LoadedApk类型,他是在activityThread初始化主线程的时候创建的。
LoadedApk#getServiceDispatcher
public final IServiceConnection getServiceDispatcher(ServiceConnection c,
Context context, Handler handler, int flags) {
synchronized (mServices) {
LoadedApk.ServiceDispatcher sd = null;
ArrayMap map = mServices.get(context);
if (map != null) {
if (DEBUG) Slog.d(TAG, "Returning existing dispatcher " + sd + " for conn " + c);
sd = map.get(c);
}
if (sd == null) {
sd = new ServiceDispatcher(c, context, handler, flags);
if (DEBUG) Slog.d(TAG, "Creating new dispatcher " + sd + " for conn " + c);
if (map == null) {
map = new ArrayMap<>();
mServices.put(context, map);
}
map.put(c, sd);
} else {
sd.validate(context, handler);
}
return sd.getIServiceConnection();
}
}
又是调用了ServiceDispatcher#getIServiceConnection
IServiceConnection getIServiceConnection() {
return mIServiceConnection;
}
mIServiceConnection是在ServiceDispatcher构造函数中创建的。
ServiceDispatcher(ServiceConnection conn,
Context context, Handler activityThread, int flags) {
mIServiceConnection = new InnerConnection(this);
mConnection = conn;
mContext = context;
mActivityThread = activityThread;
mLocation = new ServiceConnectionLeaked(null);
mLocation.fillInStackTrace();
mFlags = flags;
}
private static class InnerConnection extends IServiceConnection.Stub {
final WeakReference mDispatcher;
InnerConnection(LoadedApk.ServiceDispatcher sd) {
mDispatcher = new WeakReference(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);
}
}
}
InnerConnnection是个binder,回到开头,所以sd就是InnerConnnection对象。顺便说下,ServiceDispatcher构造函数中还传入了我们的ServiceConnection。
再来看下他的connected方法,又调用ServiceDispatcher#connected。
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) {
if (service != null) {
mConnection.onServiceConnected(name, service);
}
}
mConnection就是我们传入的ServiceConnetcion。那么第二个参数service就是我们要找的ibinder。往前追溯发现,这个ibinder其实就是从InnerConnection#connected方法中传入的。我们记住这个结论,然后继续。
接着调用了ams的bindservice方法。传入的参数有applicationThread,启动activity的时候在ams创建的token,intent。
public int bindService(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags, String callingPackage,
int userId) throws TransactionTooLargeException {
enforceNotIsolatedCaller("bindService");
// Refuse possible leaked file descriptors
if (service != null && service.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
if (callingPackage == null) {
throw new IllegalArgumentException("callingPackage cannot be null");
}
synchronized(this) {
return mServices.bindServiceLocked(caller, token, service,
resolvedType, connection, flags, callingPackage, userId);
}
}
ActiveServices#bindServiceLocked
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, final IServiceConnection connection, int flags,
String callingPackage, final int userId) throws TransactionTooLargeException {
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
s.lastActivity = SystemClock.uptimeMillis();
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
permissionsReviewRequired) != null) {
return 0;
}
}
}
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
boolean whileRestarting, boolean permissionsReviewRequired)
throws TransactionTooLargeException {
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.longVersionCode, 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);
}
// 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
// we call startProcessLocked() we will get a new isolated
// process, starting another process if we are currently waiting
// for a previous process to come up. To deal with this, we store
// in the service any current isolated process it is running in or
// waiting to have come up.
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) {
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;
}
}
if (!mPendingServices.contains(r)) {
mPendingServices.add(r);
}
}
isolated用于判断是否是开启多进程,显然我们这里为true,所以会去执行mAm#startProcessLocked,这个方法最终回去开启一个新进程。再看下面的mPendingServices,这个非常关键,他是一个List,保存了当前要创建的service对应的serviceRecord对象。他的作用后面会讲。我们先分析开启进程的方法:
AMS#startProcessLocked
final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
String hostingType, ComponentName hostingName, boolean allowWhileBooting,
boolean isolated, boolean keepIfLarge) {
return startProcessLocked(processName, info, knownToBeDead, intentFlags, hostingType,
hostingName, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge,
null /* ABI override */, null /* entryPoint */, null /* entryPointArgs */,
null /* crashHandler */);
}
final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName,
boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
final boolean success = startProcessLocked(app, hostingType, hostingNameStr, abiOverride);
}
private final boolean startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr, String abiOverride) {
return startProcessLocked(app, hostingType, hostingNameStr,
false /* disableHiddenApiChecks */, abiOverride);
}
private final boolean startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, boolean disableHiddenApiChecks, String abiOverride) {
return startProcessLocked(hostingType, hostingNameStr, entryPoint, app, uid, gids,
runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet, invokeWith,
startTime);
}
private boolean startProcessLocked(String hostingType, String hostingNameStr, String entryPoint,
ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
String seInfo, String requiredAbi, String instructionSet, String invokeWith,
long startTime) {
final ProcessStartResult startResult = startProcess(hostingType, entryPoint, app,
uid, gids, runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet,
invokeWith, startTime);
handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
startSeq, false);
}
private ProcessStartResult startProcess(String hostingType, String entryPoint,
ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
String seInfo, String requiredAbi, String instructionSet, String invokeWith,
long startTime) {
startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, invokeWith,
new String[] {PROC_START_SEQ_IDENT + app.startSeq});
}
经过多次跳转之后,通过Process#start去启动新的进程,这中间用到了socket通信,server端收到消息后会通过jni去fork一个新的进程,然后启动这个进程,开始执行ActivityThread的main方法attach方法,这个我们应该很熟悉了。其中有个步骤我们之前在分析ams的时候说过,会调用ams的attachApplication方法。
AMS#attachApplication:
@Override
public final void attachApplication(IApplicationThread thread, long startSeq) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid, callingUid, startSeq);
Binder.restoreCallingIdentity(origId);
}
}
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid, int callingUid, long startSeq) {
// Find any services that should be running in this process...
if (!badApp) {
try {
didSomething |= mServices.attachApplicationLocked(app, processName);
checkTime(startTime, "attachApplicationLocked: after mServices.attachApplicationLocked");
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown starting services in " + app, e);
badApp = true;
}
}
}
ActiveServices#attachApplicationLocked
if (mPendingServices.size() > 0) {
ServiceRecord sr = null;
try {
for (int i=0; i
还记得之前说的mPendingServices吗?这里就用到了,把刚才需要启动的service对应serviceRecord取出来,然后去启动service。
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
requestServiceBindingsLocked(r, execInFg);
}
首先代码切换到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);
}
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;
老套路,通过handler切换到主线程。一定要注意,这里的主线程是新进程的主线程!!
private void handleCreateService(CreateServiceData data) {
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = packageInfo.getAppFactory()
.instantiateService(cl, data.info.name, data.intent);
}
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.onCreate();
mServices.put(data.token, service);
}
public @NonNull Service instantiateService(@NonNull ClassLoader cl,
@NonNull String className, @Nullable Intent intent)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return (Service) cl.loadClass(className).newInstance();
}
通过反射创建了service,并且创建了和activity一样的contextImpl,调用了service#attach和onCreate。最后把service存入了叫mServices的map中。
我们再来看一下requestServiceBindingsLocked方法:
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;
}
}
}
private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
boolean execInFg, boolean rebind) throws TransactionTooLargeException {
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
r.app.repProcState);
}
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);
}
case BIND_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
handleBindService((BindServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
跳转到了新进程的主线程的handleBindService方法:
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);
}
}
}
}
从mServices取出刚才存入的service,调用他的onBind方法返回我们在service中创建的binder。
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
c.conn.connected(r.name, service, false);
}
c.conn是IServiceConnection,调用他的connected实际是跨进程调用了我们自己进程的InnerConnection的connected方法,之前分析过,这个方法最终会执行我们原来进程的serviceConnection的onServiceConnected方法。而第二个service就是service中创建的binder的代理。这里通过connected方法跨进程传递给了我们原来的进程的serviceConnection,所以传递过去的也是新进程中创建的binder的代理。我来理一下,就是新进程中的service创建了一个binder,然后传递给ams进程,ams得到的是binder代理,再传给我们原来的进程,同样也是binder代理。