Source code based on: Android R
0. Preface
In explaining the relevant knowledge points, first organize a frame diagram, followed by a frame diagram to analyze the process:
The use of binder on the Java side is roughly divided into:
- Java client Java service
- Java client native service
The registration of the service on the Java side uses ServiceManager.addService()
Use ServiceManager.getService() to obtain the service on the Java side
The entry is through ServiceManager.java, the file is located in frameworks/base/core/java/android/os/ServiceManager.java
Before analyzing addService and getService, you need to understand that Binder will eventually call the native side. Then, Java needs to go through JNI, so you need to understand the registration process of JNI before that.
1. Binder JNI Registration
Students who understand the startup process of zygote should know that zygote will first create a virtual machine when it starts, and then register the JNI interface required by the system through startReg().
frameworks/base/core/jni/AndroidRuntime.cpp
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
...
if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
env->PopLocalFrame(NULL);
return -1;
}
...
}
The registered interface is defined in:
frameworks/base/core/jni/AndroidRuntime.cpp
static const RegJNIRec gRegJNI[] = {
REG_JNI(register_com_android_internal_os_RuntimeInit),
REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
REG_JNI(register_android_os_SystemClock),
REG_JNI(register_android_util_EventLog),
REG_JNI(register_android_util_Log),
REG_JNI(register_android_util_MemoryIntArray),
...
REG_JNI(register_android_os_Binder),
...
The array is very long, so I won’t paste too much here. The binder-related registration functions are:
REG_JNI(register_android_os_Binder),
Finally, the function register_android_os_Binder() will be called:
frameworks/base/core/jni/android_util_Binder.cpp
int register_android_os_Binder(JNIEnv* env)
{
if (int_register_android_os_Binder(env) < 0)
return -1;
if (int_register_android_os_BinderInternal(env) < 0)
return -1;
if (int_register_android_os_BinderProxy(env) < 0)
return -1;
...
I won’t analyze too much for the rest. Interested students can enter to view the source code process in detail (of course, the following encounters will also be analyzed incidentally). What you need to know here is that the Binder on the Java side requires native operations. The JNI interface is all in zygote It is registered at startup.
2. ServiceManager proxy
Whether it is ServiceManager.addService() or ServiceManager.getService() , it needs to communicate with native in the end, and it needs to obtain the proxy of ServiceManager. That is, the function getIServiceManager() :
frameworks/base/core/java/android/os/ServiceManager.java
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}
// Find the service manager
sServiceManager = ServiceManagerNative
.asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
return sServiceManager;
}
Here are two main points:
- BidnerInternal.getcontextObject()
- ServiceManagerNative.asInterface()
2.1 BinderInternal.getContextObject()
frameworks/base/core/java/com/android/internal/os/BinderInternal.java
public static final native IBinder getContextObject();
This needs to call the JNI interface, and this interface registration has been explained in Section 1 , see int_register_android_os_BinderInternal() for details :
frameworks/base/core/jin/android_util_Binder.cpp
static const JNINativeMethod gBinderInternalMethods[] = {
/* name, signature, funcPtr */
{ "getContextObject", "()Landroid/os/IBinder;", (void*)android_os_BinderInternal_getContextObject },
...
}
};
static int int_register_android_os_BinderInternal(JNIEnv* env)
{
...
return RegisterMethodsOrDie(
env, kBinderInternalPathName,
gBinderInternalMethods, NELEM(gBinderInternalMethods));
}
Known by registering the gBinderInternalMethods array, the interface android_os_BinderInternal_getContextObject() is finally called:
frameworks/base/core/jin/android_util_Binder.cpp
static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
return javaObjectForIBinder(env, b);
}
It is very familiar here, what is obtained is the BpBinder of the servicemanager whose context is 0, and the BpBinder is converted into a Java BinderProxy object through the function javaObjectForIBinder() and returned.
2.1.1 javaObjectForIBinder()
frameworks/base/core/jin/android_util_Binder.cpp
jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
{
...
BinderProxyNativeData* nativeData = new BinderProxyNativeData();
nativeData->mOrgue = new DeathRecipientList;
nativeData->mObject = val;
jobject object = env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
gBinderProxyOffsets.mGetInstance, (jlong) nativeData, (jlong) val.get());
if (env->ExceptionCheck()) {
// In the exception case, getInstance still took ownership of nativeData.
return NULL;
}
...
return object;
}
Here, geInstance() in BinderProxy is mainly called through CallStaticObjectMethod() , and the global object gBinderProxyOffsets here is specified in int_register_android_os_BinderProxy() :
frameworks/base/core/jin/android_util_Binder.cpp
static int int_register_android_os_BinderProxy(JNIEnv* env)
{
gErrorOffsets.mError = MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/Error"));
gErrorOffsets.mOutOfMemory =
MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/OutOfMemoryError"));
gErrorOffsets.mStackOverflow =
MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/StackOverflowError"));
jclass clazz = FindClassOrDie(env, kBinderProxyPathName);
gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
gBinderProxyOffsets.mGetInstance = GetStaticMethodIDOrDie(env, clazz, "getInstance",
"(JJ)Landroid/os/BinderProxy;");
gBinderProxyOffsets.mSendDeathNotice =
GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
"(Landroid/os/IBinder$DeathRecipient;Landroid/os/IBinder;)V");
gBinderProxyOffsets.mNativeData = GetFieldIDOrDie(env, clazz, "mNativeData", "J");
clazz = FindClassOrDie(env, "java/lang/Class");
gClassOffsets.mGetName = GetMethodIDOrDie(env, clazz, "getName", "()Ljava/lang/String;");
return RegisterMethodsOrDie(
env, kBinderProxyPathName,
gBinderProxyMethods, NELEM(gBinderProxyMethods));
}
javaObjectForIBinder() needs to call the getInstance() interface of BinderProxy through the CallStaticObjectMethod() function, let's see:
frameworks/base/core/java/android/os/BinderProxy.java
private static BinderProxy getInstance(long nativeData, long iBinder) {
BinderProxy result;
synchronized (sProxyMap) {
try {
result = sProxyMap.get(iBinder);
if (result != null) {
return result;
}
result = new BinderProxy(nativeData);
} catch (Throwable e) {
...
}
...
}
return result;
}
So far, it can be seen that what BidnerInternal.getcontextObject() gets is the BinderProxy object whose context is 0.
2.2 ServiceManagerNative.asInterface()
framworks/base/core/java/android/os/ServiceManagerNative.java
public static IServiceManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
// ServiceManager is never local
return new ServiceManagerProxy(obj);
}
After the BinderProxy object is obtained, use asInterface to create the ServiceManager's Java-side proxy ServiceManagerProxy:
framworks/base/core/java/android/os/ServiceManagerNative.java
class ServiceManagerProxy implements IServiceManager {
public ServiceManagerProxy(IBinder remote) {
mRemote = remote;
mServiceManager = IServiceManager.Stub.asInterface(remote);
}
...
public void addService(String name, IBinder service, boolean allowIsolated, int dumpPriority)
throws RemoteException {
mServiceManager.addService(name, service, allowIsolated, dumpPriority);
}
...
private IBinder mRemote;
private IServiceManager mServiceManager;
}
mRemote is the BinderProxy object, mServiceManager is the entrance of all interfaces, the real proxy of ServiceManager, the analysis will continue below.
3. ServiceManager.addService()
Through Section 2 , we know that the binder communication on the Java side may be the server proxy obtained by the client through ServiceManager.getService() , or it may be that the server side registers the service in the ServiceManager through ServiceManager.addService() . But no matter which interface, it is operated through the code BinderProxy of ServiceManager on the java side.
This section analyzes addService():
frameworks/base/core/java/android/os/ServiceManager.java
public static void addService(String name, IBinder service, boolean allowIsolated,
int dumpPriority) {
try {
getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
} catch (RemoteException e) {
Log.e(TAG, "error in addService", e);
}
}
getIServiceManager() is the ServiceManagerProxy object returned in Section 2.2 above. As shown in the code in Section 2.2 above, addService() finally calls mServiceManager.addService() , and this mServiceManager is the BinderProxy object of context 0 mentioned above. Bring in the proxy obtained through IServiceManager as a parameter:
mServiceManager = IServiceManager.Stub.asInterface(remote);
Continue to analyze.
Usually andorid will compile *.aidl files into corresponding java files, and the final java source code is under out/soong/.intermediates/ , see the seventh article in this series of articles for details: CS under Java
The JAVA files compiled by aidl will eventually be packaged into aidl*.srcjar, so how to determine the specific files? It needs to depend on the *.aidl.d file in the frameworks folder or system folder .
If the aidl file is defined under frameworks, then you can go to the corresponding directory under the framework to find the corresponding *.aidl.d file.
This IServiceManager is the compiled class name of IServiceManager.aidl
@Override public void addService(java.lang.String name, android.os.IBinder service, boolean allowIsolated, int dumpPriority) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder(service);
...
boolean _status = mRemote.transact(Stub.TRANSACTION_addService, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().addService(name, service, allowIsolated, dumpPriority);
return;
}
_reply.readException();
}
...
}
The proxy code is relatively simple:
- Write the service to be added to Parcel through the interface writeStrongBinder();
- Communicate through mRemote.transact() to confirm whether the purpose of addService() is achieved;
- If the above fails, try addService() via getDefaultImpl();
Usually the second step is taken, and the default impl of the third step needs to be specified through the interface setDefaultImpl() .
According to Section 2.2 , mRemote is the BinderProxy object of ServiceManager's java side.
3.1 writeStrongBinder()
frameworks/base/core/java/android/os/Parcel.java
public final void writeStrongBinder(IBinder val) {
nativeWriteStrongBinder(mNativePtr, val);
}
The JNI interface is located in android_os_Parcel.cpp, and this interface is also loaded when zygote starts. There is no more analysis here, please refer to the member register_android_os_Parcel in the array gRegJNI in AndroidRuntime.cpp for details.
Finally call to JNI android_os_Parcel_writeStrongBinder():
frameworks/base/core/jni/android_os_Parcel.cpp
static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
}
}
Let's look at the core ibinderForJavaObject():
frameworks/base/core/jni/android_util_Binder.cpp
sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
{
if (obj == NULL) return NULL;
// Instance of Binder?
if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
JavaBBinderHolder* jbh = (JavaBBinderHolder*)
env->GetLongField(obj, gBinderOffsets.mObject);
return jbh->get(env, obj);
}
// Instance of BinderProxy?
if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {
return getBPNativeData(env, obj)->mObject;
}
ALOGW("ibinderForJavaObject: %p is not a Binder object", obj);
return NULL;
}
The pure service belongs to the Binder type, so it matches with gBinderOffsets.mClass when matching, and this variable is also assigned when registering, see int_register_android_os_Binder() for details .
gBinderOffsets.mObject obtains the mObject object in Binder.java (see int_register_android_os_Binder function for details), that is, the BBinderHolder pointer obtained from native when Binder is constructed:
frameworks/base/core/java/android/os/Binder.java
public Binder(@Nullable String descriptor) {
mObject = getNativeBBinderHolder();
...
}
---->
frameworks/base/core/jni/android_util_Binder.cpp
static jlong android_os_Binder_getNativeBBinderHolder(JNIEnv* env, jobject clazz)
{
JavaBBinderHolder* jbh = new JavaBBinderHolder();
return (jlong) jbh;
}
ibinderForJavaObject() In addition to the JavaBBinderHolder obtained above , it finally calls the get() interface through the holder to obtain the BBinder of the servcie:
frameworks/base/core/jni/android_util_Binder.cpp
sp<JavaBBinder> get(JNIEnv* env, jobject obj)
{
AutoMutex _l(mLock);
sp<JavaBBinder> b = mBinder.promote();
if (b == NULL) {
b = new JavaBBinder(env, obj);
...
}
return b;
}
Take a look at JavaBBinder:
frameworks/base/core/jni/android_util_Binder.cpp
class JavaBBinder : public BBinder
{
...
}
Therefore, writeStrongBinder() is ultimately the BBinder in native to write parcel into transact().
3.2 BinderProxy.transact()
See Section 5 for details
3.3 JavaBBinder.onTransact()
When the Client communicates through transact(), BBinder will eventually trigger onTransact(). For java service, the final trigger is JavaBBinder.onTransact() :
frameworks/base/core/jni/android_util_Binder.cpp
status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) override
{
...
jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags);
gBinderOffsets.mExecTransact is also assigned by int_register_android_os_Binder(), which calls execTransact() in Binder.java, but I will not elaborate here, and finally call stub.onTransact.
4. ServiceManager.getService()
Same as addService, getService is finally called into IServiceManager:
@Override public android.os.IBinder checkService(java.lang.String name) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.IBinder _result;
try {
...
boolean _status = mRemote.transact(Stub.TRANSACTION_checkService, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().checkService(name);
}
_reply.readException();
_result = _reply.readStrongBinder();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
In the same way as addService(), the binder information transmitted from the native side will be obtained through _reply.readStrongBinder() .
4.1 readStrongBinder()
Eventually it will be called to JNI android_os_Parcel.cpp
frameworks/base/core/jni/android_os_Parcel.cpp
static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
return javaObjectForIBinder(env, parcel->readStrongBinder());
}
return NULL;
}
Section 2.1 has been analyzed, and finally the BinderProxy object of the service on the java side is obtained through javaObjectForIBinder().
5. BinderProxy.transact()
frameworks/base/core/java/android/os/BinderProxy.java
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
....
try {
return transactNative(code, data, reply, flags);
} finally {
...
}
}
In fact, the core here is to call transactNative:
frameworks/base/core/jni/android_util_Binder.cpp
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{
...
status_t err = target->transact(code, *data, reply, flags);
...
}
The next step is BpBinder->transact, see the third article in this series of articles for details : service registration
6. Summary
Finally, let's summarize according to the framework diagram:
- The Java client obtains the proxy of the service through getService(), and getService() finally obtains the BinderProxy object through readStrongBinder() ;
- The Java client communicates with the service through transact, and calls BpBinder through the native interface transactNative() ;
- The Java service is registered through addService(), and addService() will call the native interface through writeStrongBinder() to register to JNI;
- When Java service is registered, JavaBBinderHolder will be created in JNI, which encapsulates JavaBBinder communicating with native , and JavaBBinder inherits from BBinder;
- The Java client call will eventually call onTransact() for the java service, and call back to the java side stub.onTransact();