Analysis of Binder communication mechanism in Android system (7) - Binder mechanism of Java layer

statement

1 Binder framework of the Java layer

  The Java framework library provides a set of Binder-related classes, so that Java code can directly use Binder to build IPC. The main classes are in the core/java/android/os/ directory and are part of the android.os package. In addition, the core/java/com/android/internal/os/ directory provides the BinderIntermal class in the com.android.internal.os package, which is a support class of Binder.
  The content supported by the Java layer Binder comes from the local and is encapsulated into the Java layer through JNI. The Binder of the Java layer and the Binder of the local layer are implemented on the same basis.

1.1 Classes related to Binder mechanism

  The main APIs of Binder are in the android.os package, they are: IInterface interface, IBinder interface, Binder class, and Parcel class.
lInterface is an interface to which actual methods are given by implementers. which only has the method:

             static sp<IBinder>  asBinder(const IInterface*);
             static sp<IBinder>  asBinder(const sp<IInterface>&);

  The asBinder() method is used to convert an IInterface into an IBinder type. IBinder is also an interface used to act as a handle for communication between the two ends of the IPC, the main content of which is the transact() method, as shown below:

    virtual status_t        transact(   uint32_t code,
                                        const Parcel& data,
                                        Parcel* reply,
                                        uint32_t flags = 0) = 0;

  transact() is used to communicate with a remote Binder, which contains 4 parameters of communication code code of integer type, parameter data of Parcel type and return value reply, and integer type flags. Several other important methods in IBinder are as follows:

	virtual const String16& getInterfaceDescriptor() const = 0;
    virtual sp<IInterface>  queryLocalInterface(const String16& descriptor);
    virtual status_t        pingBinder() = 0;

  getInterfaceDescriptor() is used to obtain the interface corresponding to this Binder, and queryLocalInterface() is used to obtain the implementation handle of the IInterface corresponding to this Binder object. pingBinder() is used to query whether the Binder still exists. Binder is a class that implements the IBinder interface, and this class is still an abstract class. The main reason is that the onTransact() method in the Binder class is not implemented, as shown below:

protected:
    virtual status_t    onTransact( uint32_t code,
                                    const Parcel& data,
                                    Parcel* reply,
                                    uint32_t flags = 0);

  IBinder.transact() and Binder.onTransact() have a corresponding relationship. The method called in IBinder.transact() will return from Binder.onTransact(), and this is a synchronous calling method.
In the program, the implementer of the IInterface interface can be mainly used to represent a specific interface; it will not directly inherit the IBinder interface, but inherit the Binder abstract class.

  Parcel is a class used to encapsulate transmitted data. The content supported in the Parcel class is the type of parameters and return values ​​​​that can be used when using Binder to communicate between processes. Parcel supports basic types, string types, Binder object types, Parcelable types, and various types of arrays. For details about Parcel, please refer to: Analysis of Binder Communication Mechanism in Android System (5) - Interprocess Data Transfer Carrier Parcel
  For an implementer of IInterface, you can call asBinder() in it, convert it into IBinder type and pass it through Parcel up. In fact, a name called writeStrongInterface() in Parcel uses this method and realizes it by calling writeStrongBinder() .

1.2 aidl description file

  The full name of AIDL is Android Interface Definition Language (Android Interface Description Language). The role of IDL is to use a special syntax to describe the interface, which is a collection of methods (functions).
  The aidl tool in Android can automatically generate a Java code file according to the aidl file, and the content is a framework for specifically using Binder for IPC .
The basic process of building a specific communication framework using the Binder mechanism at the Java layer is:

  1. Use the aidl file to declare the interface IXXX and compile it;
  2. In the service section, the real function is realized by inheriting IXXX.Stub;
  3. On the client side, use IXXXStub.asInterface() to obtain the interface of IXXX type and call it.

  In the program, the specific implementation needs to define the interface IXXX.aidl. After IXXX.aidl is added to the compilation, it will be processed by the aidl tool in Android, and a file named XXX.java will be automatically generated. It includes the IXXX interface and the IXXXStub abstract class, which are the framework of the specific Binder structure.
  Due to the automatic generation mechanism, the specific Binder framework only needs to write a relatively simple aidl file, and does not need to write the details of the code for communication through Binder IPC. For the program developer, there are three things that need to be done: aidl file, the server inherits IXXX.Stub, and the client gets the IXXX type call through IXX.Stub.asInterface() . The automatically generated IXXX.Stub is an inheritor of the Binder class.

The combination of Binder and aidl is shown in the figure below:
insert image description here
  it represents the syntax format in the aidl file and the definition of a Java interface. But what it represents is not a simple interface, but has a lot of additional content. For example, a fragment of a file named IAidITest.aidl looks like this:

package android.os;				//接口所属于的包
import android.os.AidlTest;		//需要导入的类
interface IAidlTest {
    
    
//各个方法的集合
	int intMethod(int a);
	boolean[] booleanArray(in boolean[] a0, out boolean[] al, inout boolean[] a2);
	char[] charArray(in char[] a0, out char[] al, inout char[] a2);
	String[] stringArray(in String[] a0, out String[] al, inout Stringl] a2);
	void voidSecurityException();
	int intSecurityException();
//省略部分内容
}

  interface is a keyword in the aidl file, which is used to define the name of the interface, and the main content is a collection of various methods. The syntax in aidl is basically the same as the interface definition in Java, except that the types of parameters and return values ​​are limited. in, out, and inout indicate the direction of parameter passing.
  There is another oneway keyword in the aidl file, which is used in front of the interface. It means that the caller will not wait for the return result of the method in the interface, so the return class of the method in the interface modified with oneway is void. Oneway is often used to represent interfaces that "call back" functionality.
  In the development environment of Android source code, the aidl file needs to be manually added to LOCAL_SRC_FILES before it can be processed. According to the aidl extension, the compilation system will use a special aidl tool to process it and automatically generate Java code files. As follows:

LOCAL SRC FILES += \
		{
    
    path}/*.aidl \

1.3 The use of Binder by the framework layer and the application layer

  The Binder-based communication mechanism of the Java layer can be used in both the Java framework layer and the Java application layer. Users need to use the aidl file to define the interface. The difference lies in the structure of the server and client. The typical situation of using Binder in the Java framework layer is: after the server completes an IXXXStub class, it needs to use the ServiceManager class to add it to a service with a certain name; the client also obtains this service through the ServiceManager according to the name, and converts it into an IXXX type interface to make a call.
  The typical situation of using Binder in the Java application layer is: the server declares a Service component, and in its onBind0 method, returns an IXXXStub inheritor as an IBinder type; after the client component connects to the Service component through bindService0, you can Get the IBinder handle from the implementer of the ServiceConnection interface (android.content package), convert it into IXXX type, and then call it.
  The above two cases are the typical usage of Binder, the server and client are both in the framework layer, or both in the application layer. In addition, there are some special cases.
The Java framework layer can define the Service component, just declare it in the AndroidMenifest.xml file of the resource bundle. If the client calls the Service component of the framework layer, it is basically the same as that of the typical application layer. After the aidl interface defined by the Java framework layer is usually implemented in the Java service package, the application package can directly refer to the aidl file and directly call the service implementation in the framework layer. This method can be implemented, but is not commonly used.

2 Binder usage of Java framework layer

2.1 Registration of ServiceManager and service

  The feature of using Binder in the Java framework layer is that it needs to register and obtain services through ServiceManager. The name of the service is the link between the client and the server.
The ServiceManager class is an internal class in android.os, code location: frameworks/base/core/java/android/os/ServiceManager.java

public final class ServiceManager {
    
    
    private static final String TAG = "ServiceManager";

    private static IServiceManager sServiceManager;
    private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();

    private static IServiceManager getIServiceManager() {
    
    
        if (sServiceManager != null) {
    
    
            return sServiceManager;
        }

        // Find the service manager
        sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
        return sServiceManager;
    }
	//获得服务
    public static IBinder getService(String name) {
    
    
        try {
    
    
            IBinder service = sCache.get(name);
            if (service != null) {
    
    
                return service;
            } else {
    
    
                return getIServiceManager().getService(name);
            }
        } catch (RemoteException e) {
    
    
            Log.e(TAG, "error in getService", e);
        }
        return null;
    }

	//增加服务
    public static void addService(String name, IBinder service) {
    
    
        try {
    
    
            getIServiceManager().addService(name, service, false);
        } catch (RemoteException e) {
    
    
            Log.e(TAG, "error in addService", e);
        }
    }

    public static void addService(String name, IBinder service, boolean allowIsolated) {
    
    
        try {
    
    
            getIServiceManager().addService(name, service, allowIsolated);
        } catch (RemoteException e) {
    
    
            Log.e(TAG, "error in addService", e);
        }
    }

    //检查服务
    public static IBinder checkService(String name) {
    
    
        try {
    
    
            IBinder service = sCache.get(name);
            if (service != null) {
    
    
                return service;
            } else {
    
    
                return getIServiceManager().checkService(name);
            }
        } catch (RemoteException e) {
    
    
            Log.e(TAG, "error in checkService", e);
            return null;
        }
    }

    //列出服务
    public static String[] listServices() {
    
    
        try {
    
    
            return getIServiceManager().listServices();
        } catch (RemoteException e) {
    
    
            Log.e(TAG, "error in listServices", e);
            return null;
        }
    }

    public static void initServiceCache(Map<String, IBinder> cache) {
    
    
        if (sCache.size() != 0) {
    
    
            throw new IllegalStateException("setServiceCache may only be called once");
        }
        sCache.putAll(cache);
    }
}

  The ServiceManager class contains some static methods. The so-called service is an IBinder type, usually an inheritor of the IXXX.Stub type, representing a service implementation.
  Usually, the server uses addService() to add services, and the client uses getService() to obtain and use the services. The server is usually implemented and registered by the service library. Generally, a service is a class in the com.android.server package of the Java service library.
  The ServiceManager class of the Java layer is actually implemented based on the service manager of the local layer, and the registered content is uniformly managed by the local servicemanager daemon process.

2.2 Construction details of Binder

  The main content of using Binder in the Java framework layer is the aidl file, the server and the client, plus the addition of services and the acquisition of two environments.
The usage details of Binder, the processing of aidl files and the generation of automatic classes are shown in Figure 7-4.
insert image description here
  The content in the dotted line in the figure is the existing classes in the Android framework layer. IInterface and IBinder are two interfaces, and Binder is an abstract class that implements the IBinder interface.
  First, you need to write an interface description file named IXXX.aidl, which defines a series of methods. After the aidl file is processed, the IXXX.java file will be automatically generated. IXXX in the IXXX.java file is an interface, which implements IInterface, and contains the methods defined in IXXX; IXXX.Stub is an abstract class, on the one hand, it inherits the Binder class, on the other hand, it implements the IXXX interface , but the methods in IXXX are not implemented.
  The server needs to provide a class that inherits IXXX.Stub and implements the methods defined in IXXX. Therefore, this class actually implements the IBinder interface indirectly, and an instance of this class can be registered as a service with a certain name through the addService() method of ServiceManager.
  The client needs to obtain the handle of IBinder according to the name through the getService() method of ServiceManager, and then convert this IBinder into IXXX type through the static method asInterface() in IXXX.Stub, and then call the methods in it.
  Taking the AlarmManagerService in the Java framework layer as an example, the server side is a part of the service library, while the client side is a part of the framework layer. Its interface is defined in the framework layer, and the file path is frameworks/base/core/java/android/app/IAlarmManager.aidl

package android.app;

import android.app.AlarmManager;
import android.app.IAlarmListener;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.WorkSource;

/**
 * System private API for talking with the alarm manager service.
 *
 * {@hide}
 */
interface IAlarmManager {
    
    
    /** windowLength == 0 means exact; windowLength < 0 means the let the OS decide */
    void set(String callingPackage, int type, long triggerAtTime, long windowLength,
            long interval, int flags, in PendingIntent operation, in IAlarmListener listener,
            String listenerTag, in WorkSource workSource, in AlarmManager.AlarmClockInfo alarmClock);
    boolean setTime(long millis);
    void setTimeZone(String zone);
    void remove(in PendingIntent operation, in IAlarmListener listener);
    long getNextWakeFromIdleTime();
    AlarmManager.AlarmClockInfo getNextAlarmClock(int userId);
    // update the uids being synchronized by network socket request manager
    void updateBlockedUids(int uid, boolean isBlocked);
}

  The content here defines the interface named IAlarmManager, which is located in the android.app package. Notice! This is not a simple interface in Java, it will generate complex content.
The implementation of the server is in the server package, the path is: frameworks/base/services/core/java/com/android/server/AlarmManagerService.java

class AlarmManagerService extends SystemService {
    
    
......
    private final IBinder mService = new IAlarmManager.Stub() {
    
    
	......
	        @Override
        public boolean setTime(long millis) {
    
    
            getContext().enforceCallingOrSelfPermission(
                    "android.permission.SET_TIME",
                    "setTime");

            if (mNativeData == 0) {
    
    
                Slog.w(TAG, "Not setting time since no alarm driver is available.");
                return false;
            }

            synchronized (mLock) {
    
    
                return setKernelTime(mNativeData, millis) == 0;
            }
        }

        @Override
        public void setTimeZone(String tz) {
    
    
            getContext().enforceCallingOrSelfPermission(
                    "android.permission.SET_TIME_ZONE",
                    "setTimeZone");

            final long oldId = Binder.clearCallingIdentity();
            try {
    
    
                setTimeZoneImpl(tz);
            } finally {
    
    
                Binder.restoreCallingIdentity(oldId);
            }
        }
	......
	}
......
}

  The private final IBinder mService in AlarmManagerService implements the IAlarmManager.Stub class, which has several methods defined in the AlarmManager.aidl file, which are implemented in the AarmManagerService class.
  The registration of AlarmManagerService is done in the frameworks/base/services/java/com/android/server/SystemServer.java file as follows:

            traceBeginAndSlog("StartAlarmManagerService");
            mSystemServiceManager.startService(AlarmManagerService.class);
            Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);

  The client side is part of the Java framework library. The process of obtaining the interface according to the service name is in the ContextImpl.java file in the core/java/android/app/ directory. The content fragments are as follows:

AlarmManager mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);

  The client calls the getSystemService() method of Context, and obtains a handle of AlarmManager type through the Context.ALARM_SERVICE parameter. After the client gets the handle of the AlarmManager type, it can call the methods in it.

  AlarmManager is actually just a wrapper around IAlarmManager. It has a method with the same name, and it is implemented by calling the content in IAlarmManager, which is also the external API of the alarm manager. When each part calls AlarmManager, it actually has a remote call relationship with the AlarmManagerService implemented in the service library.
  The above several parts are all the contents that need to be manually constructed when using the Binder mechanism in the Java framework layer. In addition, according to the IAlarmManager.adil file, the Java source code file will be automatically generated, and its path is as follows:

out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/app/IAlarmManager.java

The basic structure of the IAlarmManager.java file is as follows:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: frameworks/base/core/java/android/app/IAlarmManager.aidl
 */
package android.app;
/**
 * System private API for talking with the alarm manager service.
 *
 * {@hide}
 */
public interface IAlarmManager extends android.os.IInterface
{
    
    
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements android.app.IAlarmManager
{
    
    
private static final java.lang.String DESCRIPTOR = "android.app.IAlarmManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
    
    
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an android.app.IAlarmManager interface,
 * generating a proxy if needed.
 */
public static android.app.IAlarmManager asInterface(android.os.IBinder obj)
{
    
    
if ((obj==null)) {
    
    
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof android.app.IAlarmManager))) {
    
    
return ((android.app.IAlarmManager)iin);
}
return new android.app.IAlarmManager.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
    
    
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
    
    
switch (code)
{
    
    
case INTERFACE_TRANSACTION:
{
    
    
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_set:
{
    
    
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
int _arg1;
_arg1 = data.readInt();
long _arg2;
_arg2 = data.readLong();
long _arg3;
_arg3 = data.readLong();
long _arg4;
_arg4 = data.readLong();
int _arg5;
_arg5 = data.readInt();
android.app.PendingIntent _arg6;
if ((0!=data.readInt())) {
    
    
_arg6 = android.app.PendingIntent.CREATOR.createFromParcel(data);
}
else {
    
    
_arg6 = null;
}
android.app.IAlarmListener _arg7;
_arg7 = android.app.IAlarmListener.Stub.asInterface(data.readStrongBinder());
java.lang.String _arg8;
_arg8 = data.readString();
android.os.WorkSource _arg9;
if ((0!=data.readInt())) {
    
    
_arg9 = android.os.WorkSource.CREATOR.createFromParcel(data);
}
else {
    
    
_arg9 = null;
}
android.app.AlarmManager.AlarmClockInfo _arg10;
if ((0!=data.readInt())) {
    
    
_arg10 = android.app.AlarmManager.AlarmClockInfo.CREATOR.createFromParcel(data);
}
else {
    
    
_arg10 = null;
}
this.set(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7, _arg8, _arg9, _arg10);
reply.writeNoException();
return true;
}
case TRANSACTION_setTime:
{
    
    
data.enforceInterface(DESCRIPTOR);
long _arg0;
_arg0 = data.readLong();
boolean _result = this.setTime(_arg0);
reply.writeNoException();
reply.writeInt(((_result)?(1):(0)));
return true;
}
case TRANSACTION_setTimeZone:
{
    
    
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
this.setTimeZone(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_remove:
{
    
    
data.enforceInterface(DESCRIPTOR);
android.app.PendingIntent _arg0;
if ((0!=data.readInt())) {
    
    
_arg0 = android.app.PendingIntent.CREATOR.createFromParcel(data);
}
else {
    
    
_arg0 = null;
}
android.app.IAlarmListener _arg1;
_arg1 = android.app.IAlarmListener.Stub.asInterface(data.readStrongBinder());
this.remove(_arg0, _arg1);
reply.writeNoException();
return true;
}
case TRANSACTION_getNextWakeFromIdleTime:
{
    
    
data.enforceInterface(DESCRIPTOR);
long _result = this.getNextWakeFromIdleTime();
reply.writeNoException();
reply.writeLong(_result);
return true;
}
case TRANSACTION_getNextAlarmClock:
{
    
    
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
android.app.AlarmManager.AlarmClockInfo _result = this.getNextAlarmClock(_arg0);
reply.writeNoException();
if ((_result!=null)) {
    
    
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
    
    
reply.writeInt(0);
}
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements android.app.IAlarmManager
{
    
    
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
    
    
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
    
    
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
    
    
return DESCRIPTOR;
}
/** windowLength == 0 means exact; windowLength < 0 means the let the OS decide */
@Override public void set(java.lang.String callingPackage, int type, long triggerAtTime, long windowLength, long interval, int flags, android.app.PendingIntent operation, android.app.IAlarmListener listener, java.lang.String listenerTag, android.os.WorkSource workSource, android.app.AlarmManager.AlarmClockInfo alarmClock) 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.writeString(callingPackage);
_data.writeInt(type);
_data.writeLong(triggerAtTime);
_data.writeLong(windowLength);
_data.writeLong(interval);
_data.writeInt(flags);
if ((operation!=null)) {
    
    
_data.writeInt(1);
operation.writeToParcel(_data, 0);
}
else {
    
    
_data.writeInt(0);
}
_data.writeStrongBinder((((listener!=null))?(listener.asBinder()):(null)));
_data.writeString(listenerTag);
if ((workSource!=null)) {
    
    
_data.writeInt(1);
workSource.writeToParcel(_data, 0);
}
else {
    
    
_data.writeInt(0);
}
if ((alarmClock!=null)) {
    
    
_data.writeInt(1);
alarmClock.writeToParcel(_data, 0);
}
else {
    
    
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_set, _data, _reply, 0);
_reply.readException();
}
finally {
    
    
_reply.recycle();
_data.recycle();
}
}
@Override public boolean setTime(long millis) throws android.os.RemoteException
{
    
    
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
boolean _result;
try {
    
    
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeLong(millis);
mRemote.transact(Stub.TRANSACTION_setTime, _data, _reply, 0);
_reply.readException();
_result = (0!=_reply.readInt());
}
finally {
    
    
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void setTimeZone(java.lang.String zone) 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.writeString(zone);
mRemote.transact(Stub.TRANSACTION_setTimeZone, _data, _reply, 0);
_reply.readException();
}
finally {
    
    
_reply.recycle();
_data.recycle();
}
}
@Override public void remove(android.app.PendingIntent operation, android.app.IAlarmListener listener) throws android.os.RemoteException
{
    
    
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
    
    
_data.writeInterfaceToken(DESCRIPTOR);
if ((operation!=null)) {
    
    
_data.writeInt(1);
operation.writeToParcel(_data, 0);
}
else {
    
    
_data.writeInt(0);
}
_data.writeStrongBinder((((listener!=null))?(listener.asBinder()):(null)));
mRemote.transact(Stub.TRANSACTION_remove, _data, _reply, 0);
_reply.readException();
}
finally {
    
    
_reply.recycle();
_data.recycle();
}
}
@Override public long getNextWakeFromIdleTime() throws android.os.RemoteException
{
    
    
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
long _result;
try {
    
    
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getNextWakeFromIdleTime, _data, _reply, 0);
_reply.readException();
_result = _reply.readLong();
}
finally {
    
    
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public android.app.AlarmManager.AlarmClockInfo getNextAlarmClock(int userId) throws android.os.RemoteException
{
    
    
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.app.AlarmManager.AlarmClockInfo _result;
try {
    
    
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(userId);
mRemote.transact(Stub.TRANSACTION_getNextAlarmClock, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
    
    
_result = android.app.AlarmManager.AlarmClockInfo.CREATOR.createFromParcel(_reply);
}
else {
    
    
_result = null;
}
}
finally {
    
    
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_set = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_setTime = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_setTimeZone = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_remove = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
static final int TRANSACTION_getNextWakeFromIdleTime = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
static final int TRANSACTION_getNextAlarmClock = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
}
/** windowLength == 0 means exact; windowLength < 0 means the let the OS decide */
public void set(java.lang.String callingPackage, int type, long triggerAtTime, long windowLength, long interval, int flags, android.app.PendingIntent operation, android.app.IAlarmListener listener, java.lang.String listenerTag, android.os.WorkSource workSource, android.app.AlarmManager.AlarmClockInfo alarmClock) throws android.os.RemoteException;
public boolean setTime(long millis) throws android.os.RemoteException;
public void setTimeZone(java.lang.String zone) throws android.os.RemoteException;
public void remove(android.app.PendingIntent operation, android.app.IAlarmListener listener) throws android.os.RemoteException;
public long getNextWakeFromIdleTime() throws android.os.RemoteException;
public android.app.AlarmManager.AlarmClockInfo getNextAlarmClock(int userId) throws android.os.RemoteException;
}

The content defined in the IAlarmManager.java file mainly includes the following parts:

  • The IAlarmManager interface implements android.os.IInterface, and declares methods such as set() and setRepeating() defined in IAlarmManager.aidl.
  • IAlarmManager.Stub is an abstract static class that inherits from android.os.Binder and implements the IAlarmManager interface itself.
  • There is a fixed method asInterface() in IAlarmManager.Stub, the parameter type is IBinder, and the return type is IAlarmListener.
  • There are many methods inside the IAlarmManager.Stub class, which realize the communication based on the Binder mechanism. The process of automatically generating files is completed by tools and does not require developers to handle. Therefore, using the Binder mechanism in the Java layer does not need to deal with the most complicated communication part.

3 Types of parameters and return values ​​in Binder

3.1 Type overview

  Because the specific Binder interface of Java layer is usually described by aidl file. The method defined by the interface involves the issue of parameter and return value type. The methods of the aidl interface are not applicable to all Java types, and are essentially limited by the types that can be transferred in Parcel.

The types that can be used in aidl are the following aspects:

  • Basic types of the Java language (boolean, byte, char, short, int, float, and double)
  • CharSequence and String in java.lang
  • List and Map<K, V> in java.util
  • Interfaces defined in other aidl files (requires import)
  • Classes implemented by Parcelable (requires import)

  Java basic types can be directly supported by Parcel, and can be used as the parameter and return value types of the methods of the aidl interface, without the need for import. Apparently, Java can also use their array types.
  CharSequence and String are classes representing strings, which can be used as the parameter and return value types of the methods of the aidl interface, and do not need to be imported. List and Map<K, V> are template classes of container types, and these two templates can be used to encapsulate other types, as the parameter and return value types of the methods of the aidl interface, no need to import.
  In addition to the above situations, you can also use interfaces defined in other aidl files and classes implemented through Parcelable, both of which require import classes to be used.

3.2 Interfaces defined in other aidl files as types

  The interface defined in the aidl file will essentially form an implementer of the IInterface interface, and can be converted into an IBinder type for use. Therefore, the essence of using "interface defined in other aidl files" as the parameter and return type of the method of the aidl interface is to use the IBinder type to transfer in Parcel.
  In Binder's cross-process communication, if you need to use a callback-like mechanism, you usually need to use another aidl file to represent the callback interface, and use it as the parameter type of the method in the aidl file of the main interface.
  For example, in the frameworks/core/java/android/app/ directory in the code of the Java framework library, there are two aidl files related to the wallpaper manager, one represents the main interface (IWallpaperManager.aidl), and the other represents the callback interface (IWallpaperManagerCallback. aidl).

package android.app;
oneway interface IWallpaperManagerCallback {
    
    
    /**
     * Called when the wallpaper has changed
     */
    void onWallpaperChanged();
}

A snippet of IWallpaperManager.aidl's reference to this callback type looks like this:

package android.app;

import android.graphics.Rect;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.app.IWallpaperManagerCallback;
import android.app.WallpaperInfo;
import android.content.ComponentName;

/** @hide */
interface IWallpaperManager {
    
    
......
    /**
     * Get the wallpaper for a given user.
     */
    ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb, int which,
            out Bundle outParams, int userId);
......
}

  Since IWallpaperManagerCallback is used as the parameter type, you need to use import to import this interface, and the imported name is the package name plus the name of the aidl interface.
  The IWallpaperManager interface is actually implemented by a class in the Java service library, which is integrated with other aidl content in the framework layer. IWallpaperManagerCallback is implemented in the Java framework library ( WallpaperManager.java ) and passed as a parameter to the IWallpaperManager interface. In the actual running process, the implementation of IWallpaperManager in the Java service library calls the onWallpaperChanged() method implemented in the Java framework library through IWallpaperManagerCallback, which plays a role of notification.

3.3 The Parcelable protocol as a type

  Another kind of content that can be used as the parameter and return value type of the method of the aidl interface is "class based on the Parcelable protocol". The Parcelable protocol means that Parcel can be used to write and restore, and the Parcelable protocol is implemented when the Parcelable interface is implemented.

Parcelable is the Parcelable interface in the android.os package, and its basic structure is as follows:

public interface Parcelable {
    
    

    public static final int PARCELABLE_WRITE_RETURN_VALUE = 0x0001;
    public static final int PARCELABLE_ELIDE_DUPLICATES = 0x0002;
    public static final int CONTENTS_FILE_DESCRIPTOR = 0x0001;
    public int describeContents();
    public void writeToParcel(Parcel dest, int flags);		//	写入Parcel的方法

    public interface Creator<T> {
    
    							//表示“创建者”的子类
        public T createFromParcel(Parcel source);
        public T[] newArray(int size);
    }

    public interface ClassLoaderCreator<T> extends Creator<T> {
    
    
        public T createFromParcel(Parcel source, ClassLoader loader);
    }
}

Making a class support the Parcelable protocol mainly has the following steps:

  1. The class implements the Parcelable interface.
  2. Use the void writeToParcel(Parcel out) method to complete the parameter write operation.
  3. Build a Creator by implementing the Parcelable.Creator interface to realize the static creation function
  4. An aidl file can be created as an interface (optional).

In the actual application process, classes based on the Parcelable protocol are usually used to pass a class representing a complex data structure. An example implementation looks like this:

public final class Rect implements Parcelable {
    
    
	public int left;		//各个类的域
	public int top;
	public int right;
	public int bottom;

	public static final Parcelable.Creator<Rect> CREATOR 
									= new Parcelable.Creator<Rect>() {
    
    
		public Rect createFromParcel(Parcel in)(	//实现创建方法
			return new Rect(in);
		}
		
		public Rect[] newArray(int size) {
    
    			//实现建立数组方法
			return new Rect[sizel;
		}
	};
	
	public Rect() {
    
    }
	public void writeToParcel(Parcelout) {
    
    
		//依次写入各个成员
		out.writeInt(top);
		out.writeInt(left);
		out.writeInt(right);
		out.writeInt(bottom);
	}
	public void readFromParcel(Parcel in) {
    
    
		//依次读出各个成员
		left = in.readInt();
		top = in.readInt();
		right = in.readInt();
		bottom = in.readInt();
	}
}

  The essence of the Rect class is a class representing a rectangle, which contains 4 domains (attributes) of integer type. The structure of this class is not complicated though. But if you want to transfer such a class in the process of Binder transfer, obviously Parcel cannot directly know what the specific content to be transferred in it is. The purpose of making Rect implement the Parcelable interface is to establish a Parcel protocol and transfer specific data structures according to this protocol . The essence of the Parcelabl protocol is to let the class that implements the Parcelable interface decide what content should be passed by calling the writeToParcel() method.
  There are many classes implementing the Parcelable interface in the Android framework layer, which can be directly used in the aidl file as the type of parameters and return values .
The Bundle class in the android.os package is an implementer of the Parcelable interface, and its structure is as follows:

public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
    
    
......
	//实现Creator<>
    public static final Parcelable.Creator<Bundle> CREATOR =
        new Parcelable.Creator<Bundle>() {
    
    
        @Override
        public Bundle createFromParcel(Parcel in) {
    
    
            return in.readBundle();
        }

        @Override
        public Bundle[] newArray(int size) {
    
    
            return new Bundle[size];
        }
    };

    @Override
    public void writeToParcel(Parcel parcel, int flags) {
    
    
        final boolean oldAllowFds = parcel.pushAllowFds((mFlags & FLAG_ALLOW_FDS) != 0);
        try {
    
    
            super.writeToParcelInner(parcel, flags);
        } finally {
    
    
            parcel.restoreAllowFds(oldAllowFds);
        }
    }
......
}

In order to be referenced by other classes, create a frameworks/base/core/java/android/os/Bundle.aidl file for the Bundle class, the content is as follows:

package android.os;
parcelable Bundle;

  The above content indicates that the Bundle class in the android.os package is the implementer of Parcelable. In the aidl file, classes can import Bundle as the type of their parameters and return values ​​through the following statements:

import android.os.Bundle;

  Therefore, classes that implement the Parcelable interface in the Android framework layer can be directly used in the aidl file. In the actual development process, it is rare to really need to build a class that implements the Parcelable protocol. The main reasons are:

  • For the transfer of complex data types, it can be broken down into several parameters in the method;
  • Use Bundle, Uri, Bitmap and other classes that have implemented Parcelable, especially Bundle, which can contain any amount of information in a key-value manner, and can complete the description of complex data structures. Only when there is indeed a special "data structure" in the program that needs to be described separately, the Parcelable protocol needs to be implemented.

Guess you like

Origin blog.csdn.net/Xiaoma_Pedro/article/details/130961012