BinderのJava層での呼び出し詳細のAIDL

9137 ワード

Binderって何?
AIDLの紹介を始める前に,Binderについて初歩的な理解を持たなければならない.
BinderはAndroidがプロセスをまたいで通信するための方法で、この知識点が多すぎて、私は単独で話をします.
ここで、プロセス間通信に関わる以上、2つの異なるプロセスと、2つのプロセスを接続するチャネルがあることを知っておく必要があります.
この2つのプロセスの中で、サービスを提供するプロセスは私たちがそれをサービス端と呼んで、サービスを享受するプロセスは私たちがそれをクライアントと呼んで、サービス端とクライアントを接続する通路はたくさんあって、binderはその中の1つです.
AIDLって何?
Android Interface Definition Language、すなわちAndroidインタフェース定義言語
binder通信コードを生成するためのスクリプト言語と理解できる
AIDLはどう使いますか?
AndroidのDozeサービスを利用してAIDLの使用手順を分析します.
関連するファイルは次の3つです.
サービス側:DeviceIdleController.java
クライアント:DeviceIdleManager.java
チャネル(Binder):IDeviceIdleControl.aidl、ServiceManager.java クライアントを食事をしたい人にたとえ、サービス側を多くの料理を提供するレストランにたとえると、AIDLはレストランのメニューであり、ServiceManagerは各レストランを指す地図である.
一、通路(Binder)
IDeviceIdleControllerを見てみましょうaidlの具体的なコード実装
package android.os;

import android.os.IMaintenanceActivityListener;
import android.os.UserHandle;

/** @hide */
interface IDeviceIdleController {
    void addPowerSaveWhitelistApp(String name);
    ...
    void addPowerSaveTempWhitelistApp(String name, long duration, int userId, String reason);
}

これはAIDLの標準的な書き方で、このように退屈で平板に見えますが、主な構成部分はinterfaceですべてのサービス端が外部に提供するインタフェースを定義し、レストランが提供するメニューのようです.
注意深い学生がいると信じて、サービス側のインタフェースがAIDLを通じて投げ出されたが、クライアントはどのようにサービス側を見つけたのだろうか.
これはBinderのコアインプリメンテーションServiceManagerに関するもので、本編ではあまり紹介されていませんが、後章では一章を開きます.
二、サービス端
サービス側はサービスの提供者として、レストランのようで、メニューに書かれた料理も、頭を硬くして焼かなければなりません.
サービス側の具体的なコード実装のキーコードを見てみましょう
public class DeviceIdleController extends SystemService
        implements AnyMotionDetector.DeviceIdleCallback {

    BinderService mBinderService;

    private final class BinderService extends IDeviceIdleController.Stub {
        @Override public void addPowerSaveWhitelistApp(String name) {
            if (DEBUG) {
                Slog.i(TAG, "addPowerSaveWhitelistApp(name = " + name + ")");
            }
            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
                    null);
            long ident = Binder.clearCallingIdentity();
            try {
                addPowerSaveWhitelistAppInternal(name);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

        @Override public void addPowerSaveTempWhitelistApp(String packageName, long duration,
                int userId, String reason) throws RemoteException {
            addPowerSaveTempWhitelistAppChecked(packageName, duration, userId, reason);
        }
        ...
    }

    @Override
    public void onStart() {
        ...
        mBinderService = new BinderService();
        publishBinderService(Context.DEVICE_IDLE_CONTROLLER, mBinderService);
        ...
    }

}

DeviceIdleControllerはサービス側として内部クラスBinderService継承IDeviceIdleControllerを定義する.Stub,IDeviceIdleControllerを実現した.aidlで定義されているすべてのインタフェース.(疑問1:IDeviceIdleController.Stubはどこから来ましたか?)
サービス側のpublishBinderService()は、サービスマネージャのaddServiceメソッドを呼び出してサービス側をサービスマネージャに登録します.(疑問2:なぜBinderServiceがaddServiceのパラメータとして利用できるのか)
三、クライアント
クライアントを食事をしたい人と見なす場合は、レストランを見つけてメニューを開き、最後に注文して食事をする必要があります.
クライアントコードの例を用いて、以下の手順を具体的に分析します.
import android.os.IDeviceIdleController;

public class UsageStatsService extends SystemService implements
        UserUsageStatsService.StatsUpdatedListener {

    IDeviceIdleController mDeviceIdleController;
    ...
    @Override
    public void onBootPhase(int phase) {
        ...
        mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
        ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
        ...
    }
    @Override
    public void whitelistAppTemporarily(String packageName, long duration, int userId)
            throws RemoteException {
        ...
        mDeviceIdleController.addPowerSaveTempWhitelistApp(packageName, duration, userId,
                    reason.toString());
    }
    ...
}


まず、ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER)というコードは、レストランを見つけるためのステップとして機能します.
次にIDeviceIdleController.Stub.asInterfaceの方法は、レストランのメニューを開くのと同じです
最後に、mDeviceIdleController.addPowerSaveTempWhitelistAppは、注文して食事をすることに相当し、クライアントからサービス側のメソッドを呼び出すプロセス全体が終了します.
AIDLはどうやって実現したの?
ここを見て、使い方だけを知りたいと思っている学生なら、きっと楽しく使えます.しかし、AIDLの実現論理には霧が立ち込めている.
冒頭で述べたように、AIDLはBinder実装を生成するスクリプト言語にすぎないので、AIDLの実装ロジックを理解するには、AIDL生成のjavaファイルから分析する必要があります.ソースコードでは、AIDLがコンパイルされてjavaファイルがout/soongディレクトリの下に置かれ、興味のある学生はfindコマンドでディレクトリの下で以下を発掘することができます.
あまり話さないで、まず生成したIDeviceIdleControllerを貼ります.JAvaコード
/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package android.os;
// Declare any non-default types here with import statements

public interface IDeviceIdleController extends android.os.IInterface
{
	/** Local-side IPC implementation stub class. */
	public static abstract class Stub extends android.os.Binder implements android.os.IDeviceIdleController
	{
		private static final java.lang.String DESCRIPTOR = "android.os.IDeviceIdleController";
		/** Construct the stub at attach it to the interface. */
		public Stub()
		{
			this.attachInterface(this, DESCRIPTOR);
		}
		/**
		 * Cast an IBinder object into an android.os.IDeviceIdleController interface,
		 * generating a proxy if needed.
		 */
		public static android.os.IDeviceIdleController asInterface(android.os.IBinder obj)
		{
			if ((obj==null)) {
				return null;
			}
			android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
			if (((iin!=null)&&(iin instanceof android.os.IDeviceIdleController))) {
				return ((android.os.IDeviceIdleController)iin);
			}
			return new android.os.IDeviceIdleController.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
		{
			java.lang.String descriptor = DESCRIPTOR;
			switch (code)
			{
				case INTERFACE_TRANSACTION:
				{
					reply.writeString(descriptor);
					return true;
				}
				case TRANSACTION_addPowerSaveTempWhitelistApp:
				{
					data.enforceInterface(descriptor);
					java.lang.String _arg0;
					_arg0 = data.readString();
					long _arg1;
					_arg1 = data.readLong();
					int _arg2;
					_arg2 = data.readInt();
					java.lang.String _arg3;
					_arg3 = data.readString();
					this.addPowerSaveTempWhitelistApp(_arg0, _arg1, _arg2, _arg3);
					reply.writeNoException();
					return true;
				}
				default:
				{
					return super.onTransact(code, data, reply, flags);
				}
			}
		}
		private static class Proxy implements android.os.IDeviceIdleController
		{
			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;
			}
			@Override public void addPowerSaveTempWhitelistApp(java.lang.String name, long duration, int userId, java.lang.String reason) 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(name);
					_data.writeLong(duration);
					_data.writeInt(userId);
					_data.writeString(reason);
					mRemote.transact(Stub.TRANSACTION_addPowerSaveTempWhitelistApp, _data, _reply, 0);
					_reply.readException();
				}
				finally {
					_reply.recycle();
					_data.recycle();
				}
			}
		}
		static final int TRANSACTION_addPowerSaveTempWhitelistApp = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
	}
	public void addPowerSaveTempWhitelistApp(java.lang.String name, long duration, int userId, java.lang.String reason) throws android.os.RemoteException;
    ...
}

私はすでにIDeviceIdleControllerにJAvaは削除され,binderの通信フローとaddPowerSaveTempWhitelistAppの具体的な実装のみが残された.
最外層から一歩一歩分析し、IDeviceIdleControllerインタフェースはstubの抽象クラスを生成し、addPowerSaveTempWhitelistAppなどのサービス側が提供できる方法を生成しました.
これは、IDeviceIdleControllerの最初の疑問を説明することができます.StubはAIDLによって生成される.また、stubクラスはBinderを継承しているので、IBinderオブジェクトに変換できるのは、2つ目の疑問を説明しています.BinderServiceはstubクラスを継承しており、IBinderオブジェクトに変換できるからです.
次に,stubクラスの具体的な実装を肢解し始め,これもbinder通信java層実装の鍵である.
(1)コンストラクタ
コンストラクション関数ではattachInterfaceメソッドが呼び出されます.これは親Binderのメソッドです.このインタフェースをBinderに関連付けるために使用されます.ユーザーがqueryLocalInterfaceメソッドで要求したときにDESCRIPTORと比較することで、同じように現在のインタフェースに戻ります.
(2)クライアント呼び出し時に使用するasInterfaceメソッド
asInterfaceではqueryLocalInterfaceメソッドが呼び出されているが,構造関数ではqueryLocalInterfaceはインタフェースオブジェクトを取得するが,DESCRIPTORが同じであることを前提としている.したがって、プロセスと通信する場合、asInterfaceはstubオブジェクトを返し、プロセスをまたいで通信する場合、Stubを返す.Proxyオブジェクト.
(3)Proxy内部クラス
上記では、プロセス間通信時にProxyオブジェクトを取得したことを知っています.では、私たちの呼び出しもProxyクラスで発生します.
Proxyクラスでは呼び出しが必要なインタフェースaddPowerSaveTempWhitelistAppも実装されていますが、addPowerSaveTempWhitelistAppの具体的な実装をよく観察すると、彼はtransactメソッドを通じて需要を投げ出し、サービス側がtransactの要求をどのように受け取って実装するかについては、Binderの最下位実装に設計されていることがわかります.この部分は後で説明します.