接着应用篇 继续说,这次我们在系统服务里面使用 RemoteCallbackList 。 首先,你需要有一份能完整编译的安卓源码。
我这里以 Android10_r47为例。 我在 frameworks/base/core/java/com/callback/
内新增这几个文件。
服务接口的 aidl1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.callback;import com.callback.ICallbackTestCallback;interface ICallBackTestInterface { void register (ICallbackTestCallback callback) ; void unregister (ICallbackTestCallback callback) ; void callServer (String msg) ; }
服务回调的 aidl1 2 3 4 5 6 7 8 9 10 11 package com.callback;interface ICallbackTestCallback { void onReceived (String msg) ; }
aidl 的文件会自动编译成Binder对象的子类,这个编译系统已经为我们做好了
服务端实现 然后,frameworks/base/services/core/java/com/android/server/CallBackTestService.java
。在这里新建一个文件,用来作为服务端的真正实现1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 package com.android.server;import android.util.Slog;import android.os.RemoteCallbackList;import android.os.RemoteException;import android.os.Binder;import android.os.IBinder;import com.callback.ICallbackTestCallback;import com.callback.ICallBackTestInterface;public class CallBackTestService extends ICallBackTestInterface .Stub { private final String TAG = "testcallback" ; private boolean serverRunning = false ; private final RemoteCallbackList<ICallbackTestCallback> clients = new RemoteCallbackList <>(); public CallBackTestService () { serverRunning = true ; new Thread (serverRunnable).start(); } public void register (ICallbackTestCallback callback) throws RemoteException { Slog.d(TAG,"register callback from pid=" + Binder.getCallingPid()); clients.register(callback); } public void unregister (ICallbackTestCallback callback) throws RemoteException { Slog.d(TAG,"unregister callback from pid=" + Binder.getCallingPid()); clients.unregister(callback); } public void callServer (String msg) throws RemoteException { Slog.d(TAG,"received pid=" + Binder.getCallingPid()+" message: " + msg); } private Runnable serverRunnable = () ->{ int count = 0 ; while (serverRunning){ try { Thread.sleep(500 ); noteClients(Integer.toString(count++)); count = count > 10000 ? 0 : count; } catch (InterruptedException e) { e.printStackTrace(); } } }; private void noteClients (String msg) { int cb = clients.beginBroadcast(); for (int i=0 ;i<cb;i++){ try { clients.getBroadcastItem(i).onReceived(msg); } catch (RemoteException e) { e.printStackTrace(); } } clients.finishBroadcast(); } }
这里服务端的实现,实际上就和上一篇中的 CallBackServer 是一个意思。 然后封装一下服务的调用 frameworks/base/core/java/com/callback/CallBackTestManager.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package com.callback;import android.util.Slog;import android.content.Context;import android.os.RemoteException;import android.annotation.UnsupportedAppUsage;import com.callback.ICallbackTestCallback;import com.callback.ICallBackTestInterface;public class CallBackTestManager { public static final String SERVICE_NAME = "callbacktest" ; private final Context mContext; private final ICallBackTestInterface mService; public CallBackTestManager (Context context, ICallBackTestInterface service) { mContext = context; mService = service; } public void register (ICallbackTestCallback callback) { try { mService.register(callback); } catch (RemoteException e){ throw e.rethrowFromSystemServer(); } } public void unregister (ICallbackTestCallback callback) { try { mService.unregister(callback); } catch (RemoteException e){ throw e.rethrowFromSystemServer(); } } public void callserver (String msg) { try { mService.callServer(msg); } catch (RemoteException e){ throw e.rethrowFromSystemServer(); } } }
到这里,我们的大部分工作就完成了。不过此时编译成系统镜像烧录或者编译成虚拟机,还是不能调用这个服务的。 由于我们添加的类位于 frameworks/base/com 这个目录下,这个目录下的包并不在 bootclass 里面,修改一下这个文件
1 2 3 4 5 6 7 8 9 10 11 12 @@ -237,6 +237,7 @@ org\.apache\.xalan\.xslt # Packages in the google namespace across all bootclasspath jars. com\.google\.android\..* com\.google\.vr\.platform.* +com\.callback\.* ################################################### # Packages used for Android in Chrome OS
首先会遇到的问题是 selinux 的限制, 按照下面的这个 diff 文件来添加关于这个新增加的服务的规则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 @@ -5,3 +5,4 @@ type gsi_service, service_manager_type; type incidentcompanion_service, system_api_service, system_server_service, service_manager_type; type stats_service, service_manager_type; type statscompanion_service, system_server_service, service_manager_type; +type callbacktest_service, system_api_service, system_server_service, service_manager_type; @@ -220,3 +220,4 @@ wifiaware u:object_r:wifiaware_service:s0 wifirtt u:object_r:rttmanager_service:s0 window u:object_r:window_service:s0 * u:object_r:default_android_service:s0 +callbacktest u:object_r:callbacktest_service:s0 @@ -100,6 +100,7 @@ allow untrusted_app_all radio_service:service_manager find; allow untrusted_app_all app_api_service:service_manager find; allow untrusted_app_all vr_manager_service:service_manager find; allow untrusted_app_all gpu_service:service_manager find; +allow untrusted_app_all callbacktest_service:service_manager find; # Allow untrusted apps to interact with gpuservice binder_call(untrusted_app_all, gpuservice) @@ -5,3 +5,4 @@ type gsi_service, service_manager_type; type incidentcompanion_service, system_api_service, system_server_service, service_manager_type; type stats_service, service_manager_type; type statscompanion_service, system_server_service, service_manager_type; +type callbacktest_service, system_api_service, system_server_service, service_manager_type; @@ -220,3 +220,4 @@ wifiaware u:object_r:wifiaware_service:s0 wifirtt u:object_r:rttmanager_service:s0 window u:object_r:window_service:s0 * u:object_r:default_android_service:s0 +callbacktest u:object_r:callbacktest_service:s0 @@ -100,6 +100,7 @@ allow untrusted_app_all radio_service:service_manager find; allow untrusted_app_all app_api_service:service_manager find; allow untrusted_app_all vr_manager_service:service_manager find; allow untrusted_app_all gpu_service:service_manager find; +allow untrusted_app_all callbacktest_service:service_manager find; # Allow untrusted apps to interact with gpuservice binder_call(untrusted_app_all, gpuservice)
到这里之后,回到Android源码根目录,正常 make update-api && make
之后,就可以启动虚拟机了。 然后再新建一个应用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 . ├── aidl │ └── com │ └── callback │ ├── ICallbackTestCallback.aidl │ └── ICallBackTestInterface.aidl ├── AndroidManifest.xml ├── java │ └── com │ └── callback │ ├── CallBackTestManager.java │ └── demo │ └── MainActivity.java
其中,ICallbackTestCallback.aidl
, ICallBackTestInterface.aidl
, CallBackTestManager.java
三个文件都是从我们添加在源码里面的拷贝出来的,因为Android Studio的sdk里面并没有这些实现,把这些文件放到工程的目录里面,让编译可以通过。 通过 java 的类加载机制,实际上运行的时候是编译到系统里启动的那些代码,实际上我们拷贝到工程里的文件,只要接口声明与系统内的一致就可以,接口实现可以留空。就像Android SDK 里面的andrroid.jar里面的那些空接口一样。
MainActivity.java的内容如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 package com.callback.demo;import androidx.appcompat.app.AppCompatActivity;import android.content.Context;import android.content.pm.PackageManager;import android.os.Binder;import android.os.Bundle;import android.os.RemoteException;import android.util.Log;import android.view.View;import com.callback.ICallbackTestCallback;import com.callback.CallBackTestManager;import com.testcallback.demo.R;public class MainActivity extends AppCompatActivity { private final String TAG = "testcallback" ; CallBackTestManager callbackSerice = null ; private boolean bound = false ; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override protected void onResume () { super .onResume(); if (bound){ registerCallback(null ); } } @Override protected void onPause () { super .onPause(); if (bound){ unregisterCallback(null ); } } private ICallbackTestCallback callback = new ICallbackTestCallback .Stub(){ @Override public void onReceived (String msg) throws RemoteException { Log.d(TAG,"received msg: " + msg + " . from server pid=" + Binder.getCallingPid()); } }; public void registerCallback (View view) { if (callbackSerice!=null ){ callbackSerice.register(callback); } } public void unregisterCallback (View view) { if (callbackSerice!=null ){ callbackSerice.unregister(callback); } } public void bindService (View view) { if (callbackSerice == null ){ callbackSerice = (CallBackTestManager)getSystemService(CallBackTestManager.SERVICE_NAME); bound = true ; } } public void notifyService (View view) { if (callbackSerice!=null ){ callbackSerice.callserver("hello, I'm client" ); } } }
对应的资源文件如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 <?xml version="1.0" encoding="utf-8" ?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android ="http://schemas.android.com/apk/res/android" xmlns:app ="http://schemas.android.com/apk/res-auto" xmlns:tools ="http://schemas.android.com/tools" android:layout_width ="match_parent" android:layout_height ="match_parent" tools:context =".MainActivity" > <TextView android:layout_width ="wrap_content" android:layout_height ="wrap_content" android:text ="Hello World!" app:layout_constraintBottom_toBottomOf ="parent" app:layout_constraintLeft_toLeftOf ="parent" app:layout_constraintRight_toRightOf ="parent" app:layout_constraintTop_toTopOf ="parent" /> <Button android:layout_width ="wrap_content" android:layout_height ="wrap_content" android:id ="@+id/registerBotton" android:textAllCaps ="false" android:layout_marginLeft ="5dp" app:layout_constraintTop_toTopOf ="parent" app:layout_constraintLeft_toLeftOf ="parent" android:onClick ="registerCallback" android:text ="register" /> <Button android:layout_width ="wrap_content" android:layout_height ="wrap_content" android:id ="@+id/unregisterBotton" android:textAllCaps ="false" android:layout_marginLeft ="5dp" app:layout_constraintTop_toBottomOf ="@id/registerBotton" app:layout_constraintLeft_toLeftOf ="parent" android:onClick ="unregisterCallback" android:text ="unregister" /> <Button android:layout_width ="wrap_content" android:layout_height ="wrap_content" android:id ="@+id/bindservice" android:textAllCaps ="false" android:layout_marginLeft ="5dp" app:layout_constraintTop_toBottomOf ="@id/unregisterBotton" app:layout_constraintLeft_toLeftOf ="parent" android:onClick ="bindService" android:text ="Bind service" /> <Button android:layout_width ="wrap_content" android:layout_height ="wrap_content" android:id ="@+id/notifyservice" android:textAllCaps ="false" android:layout_marginLeft ="5dp" app:layout_constraintTop_toBottomOf ="@id/bindservice" app:layout_constraintLeft_toLeftOf ="parent" android:onClick ="notifyService" android:text ="Notify service" />
编译完成之后,我们将apk用AOSP的系统签名(如果你用了自己的密钥,就是你自己密钥的platform签名),AOSP的签名位于build/target/product/security/
,其内的 platform.pk8 和 platform.x509.pem 。用这两个签名给apk签名安装。这个apk需要注意声明 systemuid。 p.s: 这里使用系统签名,是因为按照本篇的流程,添加后的 CallBackTestManager 是 hide 的接口,Android 10 对hide接口做了限制,普通app直接去调的话,会报找不到接口。对于安卓10新增的hidden api 调用限制,可以前往 https://developer.android.google.cn/guide/app-compatibility/restrictions-non-sdk-interfaces 。普通应用直接调用hide的接口,可能会直接闪退,查看log,有类似下面这样的log生成
1 Accessing hidden field Landroid/os/Message;->flags:I (light greylist, JNI)
把我们新增的接口变成public的接口 这一部分,你可以理解为将我们自行增加的接口变成 SDK 接口(如果你的ROM需要通过谷歌的CTS认证,则不可以将自行增加的API变成SDK API)。 如果你有留意,在 make update-api 之后,android10_r47/frameworks/base/api/current.txt
,会出现新增的一些和aidl文件有关的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package com.callback { public interface ICallBackTestInterface extends android .os.IInterface { method public void callServer (String) throws android.os.RemoteException; method public void register (com.callback.ICallbackTestCallback) throws android.os.RemoteException; method public void unregister (com.callback.ICallbackTestCallback) throws android.os.RemoteException; } public static class ICallBackTestInterface .Default implements com .callback.ICallBackTestInterface { ctor public ICallBackTestInterface.Default(); method public android.os.IBinder asBinder () ; method public void callServer (String) throws android.os.RemoteException; method public void register (com.callback.ICallbackTestCallback) throws android.os.RemoteException; method public void unregister (com.callback.ICallbackTestCallback) throws android.os.RemoteException; } public abstract static class ICallBackTestInterface .Stub extends android .os.Binder implements com .callback.ICallBackTestInterface { ctor public ICallBackTestInterface.Stub(); method public android.os.IBinder asBinder () ; method public static com.callback.ICallBackTestInterface asInterface (android.os.IBinder) ; method public static com.callback.ICallBackTestInterface getDefaultImpl () ; method public boolean onTransact (int , android.os.Parcel, android.os.Parcel, int ) throws android.os.RemoteException; method public static boolean setDefaultImpl (com.callback.ICallBackTestInterface) ; } public interface ICallbackTestCallback extends android .os.IInterface { method public void onReceived (String) throws android.os.RemoteException; } public static class ICallbackTestCallback .Default implements com .callback.ICallbackTestCallback { ctor public ICallbackTestCallback.Default(); method public android.os.IBinder asBinder () ; method public void onReceived (String) throws android.os.RemoteException; } public abstract static class ICallbackTestCallback .Stub extends android .os.Binder implements com .callback.ICallbackTestCallback { ctor public ICallbackTestCallback.Stub(); method public android.os.IBinder asBinder () ; method public static com.callback.ICallbackTestCallback asInterface (android.os.IBinder) ; method public static com.callback.ICallbackTestCallback getDefaultImpl () ; method public boolean onTransact (int , android.os.Parcel, android.os.Parcel, int ) throws android.os.RemoteException; method public static boolean setDefaultImpl (com.callback.ICallbackTestCallback) ; } }
某种程度上,你可以认为,这个txt的内容就是SDK api,可以看到,我们新增的 CallBackTestManager 的内容并不在这个txt里面。 可以通过修改 frameworks/base/Android.bp
这个文件,修改内容如下
1 2 3 4 5 6 7 8 9 10 11 12 @@ -1237,6 +1237,7 @@ packages_to_document = [ "javax/microedition/khronos", "org/apache/http/conn", "org/apache/http/params", + "com/callback", ] // Make the api/current.txt file available for use by modules in other
修改这个文件之后,再次 make update-api
,可以看到 CallBackTestManager 的内容已经添加到 current.txt 里面去了。 再次make之后,普通应用就可以像调用安卓已有的服务那样,来使用新增的这个服务了。
复习一下 有几个小地方需要注意:
如果你添加的接口没有出现在 current.txt 里面的话,安卓10上由于谷歌新增的hidden api访问限制,普通应用是无法访问的
使用AOSP 编译出来的 signapk.jar 和 platform 签名的apk,似乎无法在虚拟机上安装为系统应用,我是把 platform key 转换成 Android Studio 使用的 keystore 来使用的。