Análise do mecanismo de comunicação Binder no sistema Android (7) - Mecanismo Binder da camada Java

declaração

1 Estrutura Binder da camada Java

  A biblioteca de estrutura Java fornece um conjunto de classes relacionadas ao Binder, para que o código Java possa usar diretamente o Binder para construir o IPC. As classes principais estão no diretório core/java/android/os/ e fazem parte do pacote android.os. Além disso, o diretório core/java/com/android/internal/os/ fornece a classe BinderIntermal no pacote com.android.internal.os, que é uma classe de suporte do Binder.
  O conteúdo suportado pelo Binder da camada Java vem do local e é encapsulado na camada Java por meio do JNI. O Binder da camada Java e o Binder da camada local são implementados da mesma forma.

1.1 Classes relacionadas ao mecanismo Binder

  As principais APIs do Binder estão no pacote android.os, são elas: interface IInterface, interface IBinder, classe Binder e classe Parcel.
lInterface é uma interface para a qual os métodos reais são fornecidos pelos implementadores. que só tem o método:

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

  O método asBinder() é usado para converter um IInterface em um tipo IBinder. O IBinder também é uma interface utilizada para atuar como handle para a comunicação entre as duas pontas do IPC, cujo conteúdo principal é o método transact(), conforme imagem abaixo:

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

  transact() é usado para se comunicar com um Binder remoto, que contém 4 parâmetros de código de comunicação de tipo inteiro, dados de parâmetro de tipo Parcel e resposta de valor de retorno e sinalizadores de tipo inteiro. Vários outros métodos importantes no IBinder são os seguintes:

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

  getInterfaceDescriptor() é usado para obter a interface correspondente a este Binder, e queryLocalInterface() é usado para obter o identificador de implementação do IInterface correspondente a este objeto Binder. pingBinder() é usado para consultar se o Binder ainda existe. Binder é uma classe que implementa a interface IBinder e ainda é uma classe abstrata. O principal motivo é que o método onTransact() da classe Binder não está implementado, conforme mostrado abaixo:

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

  IBinder.transact() e Binder.onTransact() têm um relacionamento correspondente. O método chamado em IBinder.transact() retornará de Binder.onTransact(), e este é um método de chamada síncrona.
No programa, o implementador da interface IInterface pode ser usado principalmente para representar uma interface específica; ele não herdará diretamente a interface IBinder, mas herdará a classe abstrata Binder.

  Parcel é uma classe usada para encapsular dados transmitidos. O conteúdo suportado na classe Parcel é o tipo de parâmetros e valores de retorno que podem ser usados ​​ao usar o Binder para comunicação entre processos. Parcel oferece suporte a tipos básicos, tipos de string, tipos de objeto Binder, tipos Parcelable e vários tipos de matrizes. Para obter detalhes sobre Parcel, consulte: Analysis of Binder Communication Mechanism in Android System (5) - Inter-process Data Transfer Carrier Parcel
  Para um implementador de IInterface, você pode chamar asBinder(), convertê-lo no tipo IBinder e passá-lo Parcelar. Na verdade, um nome chamado writeStrongInterface() em Parcel usa esse método e o realiza chamando writeStrongBinder() .

1.2 arquivo de descrição aidl

  O nome completo de AIDL é Android Interface Definition Language (Android Interface Description Language). O papel do IDL é usar uma sintaxe especial para descrever a interface, que é uma coleção de métodos (funções).
  A ferramenta aidl no Android pode gerar automaticamente um arquivo de código Java de acordo com o arquivo aidl, e o conteúdo é uma estrutura para usar especificamente o Binder for IPC .
O processo básico de construção de uma estrutura de comunicação específica usando o mecanismo Binder na camada Java é:

  1. Use o arquivo aidl para declarar a interface IXXX e compilá-la;
  2. Na seção de serviço, a função real é realizada herdando IXXX.Stub;
  3. No lado do cliente, use IXXXStub.asInterface() para obter a interface do tipo IXXX e chamá-la.

  No programa, a implementação específica precisa definir a interface IXXX.aidl. Depois que IXXX.aidl for adicionado à compilação, ele será processado pela ferramenta aidl no Android e um arquivo chamado XXX.java será gerado automaticamente. Ele inclui a interface IXXX e a classe abstrata IXXXStub, que são a estrutura da estrutura Binder específica.
  Devido ao mecanismo de geração automática, a estrutura específica do Binder precisa apenas escrever um arquivo aidl relativamente simples e não precisa escrever os detalhes do código para comunicação através do Binder IPC. Para o desenvolvedor do programa, há três coisas que precisam ser feitas: arquivo aidl, o servidor herda IXXX.Stub e o cliente obtém a chamada do tipo IXXX por meio de IXX.Stub.asInterface() . O IXXX.Stub gerado automaticamente é um herdeiro da classe Binder.

A combinação de Binder e aidl é mostrada na figura abaixo:
insira a descrição da imagem aqui
  representa o formato da sintaxe no arquivo aidl e a definição de uma interface Java. Mas o que ele representa não é uma interface simples, mas tem muito conteúdo adicional. Por exemplo, um fragmento de um arquivo chamado IAidITest.aidl tem a seguinte aparência:

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 é uma palavra-chave no arquivo aidl, que é usada para definir o nome da interface, e o conteúdo principal é uma coleção de vários métodos. A sintaxe em aidl é basicamente a mesma da definição de interface em Java, exceto que os tipos de parâmetros e valores de retorno são limitados. in, out e inout indicam a direção da passagem do parâmetro.
  Há outra palavra-chave unidirecional no arquivo aidl, que é usada na frente da interface. Isso significa que o chamador não esperará pelo resultado de retorno do método na interface, portanto, a classe de retorno do método na interface modificada com oneway é nula. Oneway é freqüentemente usado para representar interfaces que "chamam de volta" a funcionalidade.
  No ambiente de desenvolvimento do código-fonte do Android, o arquivo aidl precisa ser adicionado manualmente a LOCAL_SRC_FILES antes de poder ser processado. De acordo com a extensão aidl, o sistema de compilação usará uma ferramenta aidl especial para processá-lo e gerar automaticamente arquivos de código Java. Do seguinte modo:

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

1.3 O uso do Binder pela camada de framework e pela camada de aplicação

  O mecanismo de comunicação baseado em Binder da camada Java pode ser usado tanto na camada de estrutura Java quanto na camada de aplicativo Java. Os usuários precisam usar o arquivo aidl para definir a interface. A diferença está na estrutura do servidor e do cliente. A situação típica de uso do Binder na camada do framework Java é: após o servidor completar uma classe IXXXStub, ele precisa usar a classe ServiceManager para adicioná-lo a um serviço com um determinado nome; o cliente também obtém este serviço através do ServiceManager de acordo com o nome e o converte em uma interface do tipo IXXX para fazer uma chamada.
  A situação típica de uso do Binder na camada de aplicativo Java é: o servidor declara um componente Service e, em seu método onBind0, retorna um herdeiro IXXXStub como um tipo IBinder; após o componente cliente se conectar ao componente Service por meio do bindService0, você pode obter o identificador IBinder do implementador da interface ServiceConnection (pacote android.content), converta-o no tipo IXXX e, em seguida, chame-o.
  Os dois casos acima são o uso típico do Binder, o servidor e o cliente estão ambos na camada de estrutura ou ambos na camada de aplicativo. Além disso, existem alguns casos especiais.
A camada do framework Java pode definir o componente Service, basta declará-lo no arquivo AndroidMenifest.xml do pacote de recursos. Se o cliente chamar o componente de serviço da camada de estrutura, é basicamente o mesmo da camada de aplicativo típica. Depois que a interface aidl definida pela camada de estrutura Java é geralmente implementada no pacote de serviço Java, o pacote de aplicativos pode se referir diretamente ao arquivo aidl e chamar diretamente a implementação do serviço na camada de estrutura. Este método pode ser implementado, mas não é comumente usado.

2 Uso do fichário da camada de estrutura Java

2.1 Cadastro de ServiceManager e serviço

  O recurso de usar o Binder na camada de estrutura Java é que ele precisa se registrar e obter serviços por meio do ServiceManager. O nome do serviço é o link entre o cliente e o servidor.
A classe ServiceManager é uma classe interna em android.os, localização do código: 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);
    }
}

  A classe ServiceManager contém alguns métodos estáticos. O chamado serviço é um tipo IBinder, geralmente um herdeiro do tipo IXXX.Stub, representando uma implementação de serviço.
  Normalmente, o servidor usa addService() para adicionar serviços e o cliente usa getService() para obter e usar os serviços. O servidor geralmente é implementado e registrado pela biblioteca de serviços. Geralmente, um serviço é uma classe no pacote com.android.server da biblioteca de serviços Java.
  A classe ServiceManager da camada Java é realmente implementada com base no gerenciador de serviço da camada local, e o conteúdo registrado é gerenciado uniformemente pelo processo daemon servicemanager local.

2.2 Detalhes construtivos do Binder

  O conteúdo principal do uso do Binder na camada do framework Java é o arquivo aidl, o servidor e o cliente, mais a adição de serviços e a aquisição de dois ambientes.
Os detalhes de uso do Binder, o processamento de arquivos aidl e a geração de classes automáticas são mostrados na Figura 7-4.
insira a descrição da imagem aqui
  O conteúdo na linha pontilhada na figura são as classes existentes na camada de estrutura do Android. IInterface e IBinder são duas interfaces, e Binder é uma classe abstrata que implementa a interface IBinder.
  Primeiro, você precisa escrever um arquivo de descrição de interface chamado IXXX.aidl, que define uma série de métodos. Após o processamento do arquivo aidl, o arquivo IXXX.java será gerado automaticamente. IXXX no arquivo IXXX.java é uma interface que implementa IInterface e contém os métodos definidos em IXXX; IXXX.Stub é uma classe abstrata, por um lado, herda a classe Binder, por outro lado, implementa o Interface IXXX , mas os métodos em IXXX não são implementados.
  O servidor precisa fornecer uma classe que herde IXXX.Stub e implemente os métodos definidos em IXXX. Portanto, essa classe realmente implementa a interface IBinder indiretamente e uma instância dessa classe pode ser registrada como um serviço com um determinado nome por meio do método addService() de ServiceManager.
  O cliente precisa obter o identificador de IBinder de acordo com o nome por meio do método getService() do ServiceManager e, em seguida, converter esse IBinder no tipo IXXX por meio do método estático asInterface() em IXXX.Stub e, em seguida, chamar os métodos nele.
  Tomando como exemplo o AlarmManagerService na camada de framework Java, o lado do servidor faz parte da biblioteca de serviços, enquanto o lado do cliente faz parte da camada de framework. Sua interface é definida na camada do framework, e o caminho do arquivo é 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);
}

  O conteúdo aqui define a interface denominada IAlarmManager, localizada no pacote android.app. Perceber! Esta não é uma interface simples em Java, ela irá gerar um conteúdo complexo.
A implementação do servidor está no pacote do servidor, o caminho é: 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);
            }
        }
	......
	}
......
}

  O IBinder mService final privado em AlarmManagerService implementa a classe IAlarmManager.Stub, que possui vários métodos definidos no arquivo AlarmManager.aidl, que são implementados na classe AarmManagerService.
  O cadastro do AlarmManagerService é feito no arquivo frameworks/base/services/java/com/android/server/SystemServer.java da seguinte forma:

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

  O lado do cliente faz parte da biblioteca do framework Java. O processo de obtenção da interface de acordo com o nome do serviço está no arquivo ContextImpl.java no diretório core/java/android/app/. Os fragmentos de conteúdo são os seguintes:

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

  O cliente chama o método getSystemService() de Context, e obtém um handle do tipo AlarmManager através do parâmetro Context.ALARM_SERVICE. Depois que o cliente obtém o identificador do tipo AlarmManager, ele pode chamar os métodos nele.

  Na verdade, o AlarmManager é apenas um wrapper do IAlarmManager. Possui um método com o mesmo nome e é implementado chamando o conteúdo em IAlarmManager, que também é a API externa do gerenciador de alarmes. Quando cada parte chama o AlarmManager, na verdade ela tem um relacionamento de chamada remota com o AlarmManagerService implementado na biblioteca de serviço.
  As várias partes acima são todos os conteúdos que precisam ser construídos manualmente ao usar o mecanismo Binder na camada de estrutura Java. Além disso, de acordo com o arquivo IAlarmManager.adil, o arquivo de código-fonte Java será gerado automaticamente e seu caminho é o seguinte:

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

A estrutura básica do arquivo IAlarmManager.java é a seguinte:

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

O conteúdo definido no arquivo IAlarmManager.java inclui principalmente as seguintes partes:

  • A interface IAlarmManager implementa android.os.IInterface e declara métodos como set() e setRepeating() definidos em IAlarmManager.aidl.
  • IAlarmManager.Stub é uma classe estática abstrata que herda de android.os.Binder e implementa a própria interface IAlarmManager.
  • Existe um método fixo asInterface() em IAlarmManager.Stub, o tipo de parâmetro é IBinder e o tipo de retorno é IAlarmListener.
  • Existem vários métodos dentro da classe IAlarmManager.Stub, que realizam a comunicação com base no mecanismo Binder. O processo de geração automática de arquivos é concluído por ferramentas e não requer o manuseio de desenvolvedores. Portanto, usar o mecanismo Binder na camada Java não precisa lidar com a parte mais complicada da comunicação.

3 Tipos de parâmetros e valores de retorno no Binder

3.1 Visão geral do tipo

  Porque a interface Binder específica da camada Java geralmente é descrita pelo arquivo aidl. O método definido pela interface envolve a questão do tipo de parâmetro e valor de retorno. Os métodos da interface aidl não são aplicáveis ​​a todos os tipos Java e são essencialmente limitados pelos tipos que podem ser transferidos no Parcel.

Os tipos que podem ser usados ​​no aidl são os seguintes aspectos:

  • Tipos básicos da linguagem Java (boolean, byte, char, short, int, float e double)
  • CharSequence e String em java.lang
  • List e Map<K, V> em java.util
  • Interfaces definidas em outros arquivos aidl (requer importação)
  • Classes implementadas por Parcelable (requer importação)

  Os tipos básicos de Java podem ser suportados diretamente pelo Parcel e podem ser usados ​​como os tipos de parâmetro e valor de retorno dos métodos da interface aidl, sem a necessidade de importação. Aparentemente, Java também pode usar seus tipos de matriz.
  CharSequence e String são classes que representam strings, que podem ser usadas como tipos de parâmetro e valor de retorno dos métodos da interface aidl, e não precisam ser importadas. List e Map<K, V> são classes template de tipos container, e esses dois templates podem ser usados ​​para encapsular outros tipos, como os tipos de parâmetro e valor de retorno dos métodos da interface aidl, sem necessidade de importação.
  Além das situações acima, você também pode usar interfaces definidas em outros arquivos aidl e classes implementadas através de Parcelable, sendo que ambos requerem o uso de classes de importação.

3.2 Interfaces definidas em outros arquivos aidl como tipos

  A interface definida no arquivo aidl formará essencialmente um implementador da interface IInterface e pode ser convertida em um tipo IBinder para uso. Portanto, a essência de usar "interface definida em outros arquivos aidl" como parâmetro e tipo de retorno do método da interface aidl é usar o tipo IBinder para transferir em Parcel.
  Na comunicação entre processos do Binder, se você precisar usar um mecanismo semelhante ao callback, geralmente precisará usar outro arquivo aidl para representar a interface callback e usá-lo como o tipo de parâmetro do método no arquivo aidl da interface principal .
  Por exemplo, no diretório frameworks/core/java/android/app/ no código da biblioteca Java framework, existem dois arquivos aidl relacionados ao gerenciador de papel de parede, um representa a interface principal (IWallpaperManager.aidl) e o outro representa a interface de retorno de chamada (IWallpaperManagerCallback. aidl).

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

Um trecho da referência de IWallpaperManager.aidl para esse tipo de retorno de chamada se parece com isto:

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

  Como IWallpaperManagerCallback é usado como o tipo de parâmetro, você precisa usar import para importar essa interface, e o nome importado é o nome do pacote mais o nome da interface aidl.
  A interface IWallpaperManager é realmente implementada por uma classe na biblioteca de serviço Java, que é integrada a outro conteúdo de ajuda na camada de estrutura. IWallpaperManagerCallback é implementado na biblioteca de estrutura Java ( WallpaperManager.java ) e passado como um parâmetro para a interface IWallpaperManager. No processo de execução real, a implementação de IWallpaperManager na biblioteca de serviço Java chama o método onWallpaperChanged() implementado na biblioteca de estrutura Java por meio de IWallpaperManagerCallback, que desempenha um papel de notificação.

3.3 O protocolo Parcelable como um tipo

  Outro tipo de conteúdo que pode ser usado como parâmetro e tipo de valor de retorno do método da interface aidl é "classe baseada no protocolo Parcelable". O protocolo Parcelable significa que o Parcel pode ser usado para gravar e restaurar, e o protocolo Parcelable é implementado quando a interface Parcelable é implementada.

Parcelable é a interface Parcelable no pacote android.os, e sua estrutura básica é a seguinte:

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

Tornar uma classe compatível com o protocolo Parcelable tem principalmente os seguintes passos:

  1. A classe implementa a interface Parcelable.
  2. Use o método void writeToParcel(Parcel out) para concluir a operação de gravação do parâmetro.
  3. Crie um Creator implementando a interface Parcelable.Creator para realizar a função de criação estática
  4. Um arquivo aidl pode ser criado como uma interface (opcional).

No processo de aplicação real, as classes baseadas no protocolo Parcelable são geralmente usadas para passar uma classe que representa uma estrutura de dados complexa. Um exemplo de implementação se parece com isto:

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

  A essência da classe Rect é uma classe que representa um retângulo, que contém 4 domínios (atributos) do tipo inteiro. A estrutura desta classe não é complicada. Mas se você deseja transferir tal classe no processo de transferência do Binder, obviamente o Parcel não pode saber diretamente qual é o conteúdo específico a ser transferido nele. O objetivo de fazer Rect implementar a interface Parcelable é estabelecer um protocolo Parcel e transferir estruturas de dados específicas de acordo com este protocolo . A essência do protocolo Parcelabl é permitir que a classe que implementa a interface Parcelable decida qual conteúdo deve ser passado chamando o método writeToParcel().
  Existem muitas classes que implementam a interface Parcelable na camada de estrutura do Android, que podem ser usadas diretamente no arquivo aidl como o tipo de parâmetros e valores de retorno .
A classe Bundle no pacote android.os é um implementador da interface Parcelable, e sua estrutura é a seguinte:

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

Para ser referenciado por outras classes, crie um arquivo frameworks/base/core/java/android/os/Bundle.aidl para a classe Bundle, o conteúdo é o seguinte:

package android.os;
parcelable Bundle;

  O conteúdo acima indica que a classe Bundle no pacote android.os é o implementador de Parcelable. No arquivo aidl, as classes podem importar Bundle conforme o tipo de seus parâmetros e retornar valores através das seguintes declarações:

import android.os.Bundle;

  Portanto, as classes que implementam a interface Parcelable na camada do framework Android podem ser usadas diretamente no arquivo aidl.No processo de desenvolvimento real, é raro realmente precisar construir uma classe que implemente o protocolo Parcelable. Os principais motivos são:

  • Para a transferência de tipos de dados complexos, pode ser dividido em vários parâmetros no método;
  • Use Bundle, Uri, Bitmap e outras classes que tenham implementado Parcelable, especialmente Bundle, que pode conter qualquer quantidade de informação de forma chave-valor, e pode completar a descrição de estruturas de dados complexas. Somente quando existe de fato uma "estrutura de dados" especial no programa que precisa ser descrita separadamente, o protocolo Parcelable precisa ser implementado.

Acho que você gosta

Origin blog.csdn.net/Xiaoma_Pedro/article/details/130961012
Recomendado
Clasificación