Android 源碼編譯AIDL 使用實例講解及Android Studio AIDL的調用詳解

Android日常開發中工作中經常到遇到這種情況,一些不能與源碼編譯的第三方APP想調用一些framework 層或者platfom 簽名應用纔有權限調用的一些方法,比如更改系統時間,更改系統字體,寫入系統級屬性,開啓關閉系統設備等,這些方法如果沒有底層去開接口支持,第三方應用真是一愁莫展。本文將以一個實例講解如何以AIDL的方式給上層應用開調用底層方法的接口。

接口需求:

將Android源碼frameworks/base/core/java/android/service/persistentdata/PersistentDataBlockManager.java類透出接口給上層調用,關於PersistentDataBlockManager的作用,有興趣的朋友,可以參考:Android L集成新特性之恢復出廠設置保護之如何實現,類似蘋果ID的遠程控制功能

先來看AIDL的簡介:


AIDL可以跨進程訪問其他應用程序,和其他應用程序通訊,說到進程間通訊,很多技術都可以訪問,比如廣播應用B向A發送指定Action的廣播,A就能收到信息,這樣也能看成不同應用之間完成了通訊(但是這種通訊是單向的);還如ContentProvider,通過URI接口暴露數據給其他應用訪問;但是這種都算不上是應用之間的通訊。

Note: Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL.

“只有當你允許來自不同的客戶端訪問你的服務並且需要處理多線程問題時你才必須使用AIDL”,其他情況下你都可以選擇其他方法,如使用Messager,也能跨進程通訊。可見AIDL是處理多線程、多客戶端併發訪問的。而Messager是單線程處理。

一. 在參與源碼編譯的應用平臺項目中定義AIDL Server

1. 創建一個ADIL 接口文件

在項目中新建包名:com.asus.cnfindphone.service.persistentdata
包下新建文件IDataBlockService.aidl
package com.asus.cnfindphone.service.persistentdata;

// Declare any non-default types here with import statements

interface IDataBlockService {
    byte[] read();
    int write(in byte[] data);
    void wipe();
}

IDataBlockService 接口提供三個接口方法 讀 ,寫 ,擦除。

2. 創建AIDL Service實現類

包名下創建類文件 DataBlockService.java
目的是要將PersistentDataBlockManager類透出去給上層應用讀寫擦
package com.asus.cnfindphone.service.persistentdata;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import android.service.persistentdata.PersistentDataBlockManager;

/**
 * Created by Qinghua_Liu on 2017-2-28.
 */
public class DataBlockService extends Service {
    private static String TAG = "Qinghua";

    @Override
    public void onCreate() {
        super.onCreate();
    }

    private boolean bInPSTList(String calling) {
        boolean bRes = false;
        if (calling.equalsIgnoreCase("asus.findmyphone")) {
            bRes = true;
        }
        Log.d(TAG, "bInPSTList==" + bRes);
        return bRes;
    }

    @Override
    public IBinder onBind(Intent intent) {
        //String callingApp = intent.getComponent().getPackageName();
        return mBind;
    }

    private final IDataBlockService.Stub mBind = new IDataBlockService.Stub() {
        @Override
        public byte[] read() throws RemoteException {
            byte[] bytes = null;
            Log.d(TAG, "DataBlockService.read()");
            String callingApp = getApplicationContext().getPackageManager().getNameForUid(Binder.getCallingUid());
            Log.d(TAG, "callingApp==" + callingApp);
            if (bInPSTList(callingApp)) {
                PersistentDataBlockManager pdbManager = (PersistentDataBlockManager) getApplicationContext().getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
                bytes = pdbManager.read();
                //String id ="shenshiid";
                //return id.getBytes();
            }
            Log.d(TAG, "pdbManager.read()==" + new String(bytes));
            return bytes;
        }

        @Override
        public int write(byte[] data) throws RemoteException {
            Log.d(TAG, "DataBlockService.write()" + new String(data));
            String callingApp = getApplicationContext().getPackageManager().getNameForUid(Binder.getCallingUid());
            Log.d(TAG, "callingApp==" + callingApp);
            if (bInPSTList(callingApp)) {
                if (data != null && data.length > 0) {
                    PersistentDataBlockManager pdbManager = (PersistentDataBlockManager) getApplicationContext().getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
                    pdbManager.write(data);
                }
            }
            return 0;
        }

        @Override
        public void wipe() throws RemoteException {

        }
    };
}




3.Manifest 靜態註冊Service


<service android:name=".service.persistentdata.DataBlockService">
<intent-filter>
<action android:name="com.asus.cnfindphone.service.persistentdata.DataBlockService"/>
</intent-filter>
</service>

4. 過濾Service 調用者package:


細心的讀者可能 已經發現 2 步驟中的代碼:
@Override
        public byte[] read() throws RemoteException {
            byte[] bytes = null;
            Log.d(TAG, "DataBlockService.read()");
            String callingApp = getApplicationContext().getPackageManager().getNameForUid(Binder.getCallingUid());
            Log.d(TAG, "callingApp==" + callingApp);
            if (bInPSTList(callingApp)) {
                PersistentDataBlockManager pdbManager = (PersistentDataBlockManager) getApplicationContext().getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
                bytes = pdbManager.read();
                //String id ="shenshiid";
                //return id.getBytes();
            }
            Log.d(TAG, "pdbManager.read()==" + new String(bytes));
            return bytes;
        }



private boolean bInPSTList(String calling) {
        boolean bRes = false;
        if (calling.equalsIgnoreCase("asus.findmyphone")) {
            bRes = true;
        }
        Log.d(TAG, "bInPSTList==" + bRes);
        return bRes;
    }

代碼應該很好明白:只允許asus.findmyphone package 調用接口,如果需要讓多個包調用可以寫入config.xml 定義字串array
需要注意的是獲取Service調用者真實包名,防止僞包名:
String callingApp = getApplicationContext().getPackageManager().getNameForUid(Binder.getCallingUid());

5.源碼編譯AIDL Android.mk的寫法:

LOCAL_SRC_FILES := $(call all-java-files-under)

LOCAL_SRC_FILES += \
src/com/asus/cnfindphone/service/persistentdata/IDataBlockService.aidl

6.源碼編譯安裝平臺應用。


二. 在Android Studio 中第三方應用調用AIDL接口

上面已經將AIDL Server 部分做完,下面將講述如果在Android Studio 環境個調試的第三方應用去實現上面AIDL的調用Client

1. Android Studio如何正確導入aidl

Android Studio與Eclipse及源碼文件 目錄結構不一樣,其aidl 文件目錄需要單獨在app/src/main/ 目錄下創建aidl 目錄
再在此aidl 目錄下創建將要導入的xxx.aidl的包名,最後將xxx.aidl 文件導入,描述的複雜,還是看圖吧:
注意這裏的IDataBlockService.aidl文件是server 提供的

2.Activity 類中調用接口:

import com.asus.cnfindphone.service.persistentdata.IDataBlockService;

private IDataBlockService dataBlockService;
    private  ServiceConnection conn;
    private void initService(){
        conn = new ServiceConnection() {

            @Override
            public void onServiceDisconnected(ComponentName name) {
                Log.d("Qinghua","onServiceDisconnected! ");
            }

            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                dataBlockService = IDataBlockService.Stub.asInterface((IBinder)service);
                Log.d("Qinghua","bind success! ");
            }
        };
        Intent i = new Intent("com.asus.cnfindphone.service.persistentdata.DataBlockService");
        i.setPackage("com.asus.cnfindphone");
        bindService(i, conn, Context.BIND_AUTO_CREATE);
    }
protected void onCreate(Bundle paramBundle) {
        super.onCreate(paramBundle);
        setContentView(R.layout.activity_main);
        initService();
this.location.setOnClickListener(new View.OnClickListener() {
            public void onClick(View paramView) {
                try {
                    dataBlockService.write("cissy".getBytes());
                }catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
        this.test.setOnClickListener(new View.OnClickListener() {
            public void onClick(View paramView) {
                try {
                    byte[] bytes = dataBlockService.read();
                    Log.d("Qinghua","result:"+new String(bytes));
                }catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });

}
@Override protected void onDestroy() 
{ 
super.onDestroy(); 
unbindService(conn); 
dataBlockService = null; 
}







三.調試與Log分析:


Log片斷及分析:


#往block 寫入數據 cissy
03-01 13:45:00.211 5655-5669/com.asus.cnfindphone D/Qinghua: DataBlockService.write()cissy

#調用者包名過濾
03-01 13:45:00.212 5655-5669/com.asus.cnfindphone D/Qinghua: callingApp==asus.findmyphone
03-01 13:45:00.212 5655-5669/com.asus.cnfindphone D/Qinghua: bInPSTList==true

#調用寫入成功,system 級權限允許
03-01 13:45:00.213 3001-3127/system_process E/Qinghua: mAllowedUid==1000callingUid==1000

#讀block 數據
03-01 13:45:04.315 5655-5668/com.asus.cnfindphone D/Qinghua: DataBlockService.read()

#調用者包名過濾
03-01 13:45:04.316 5655-5668/com.asus.cnfindphone D/Qinghua: callingApp==asus.findmyphone
03-01 13:45:04.316 5655-5668/com.asus.cnfindphone D/Qinghua: bInPSTList==true

#調用讀數據,system 級權限允許
03-01 13:45:04.316 3001-5234/system_process E/Qinghua: mAllowedUid==1000callingUid==1000

#讀取之前寫的數據成功
03-01 13:45:04.327 5655-5668/com.asus.cnfindphone D/Qinghua: pdbManager.read()==cissy
03-01 13:45:04.327 6488-6488/asus.findmyphone D/Qinghua: result:cissy

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章