接着上回的继续说,这次我们在系统服务里面使用 RemoteCallbackList 。
首先,你需要有一份能完整编译的安卓源码。
我这里以 Android10_r47为例。
我在 frameworks/base/core/java/com/callback/
内几个文件。
- 服务接口的 aidl
// ICallBackTestInterface.aidl
package com.callback;
// Declare any non-default types here with import statements
import com.callback.ICallbackTestCallback;
interface ICallBackTestInterface {
// 向服务端注册客户端回调
void register(ICallbackTestCallback callback);
// 向服务端注销客户端回调
void unregister(ICallbackTestCallback callback);
// 向服务端发送消息
void callServer(String msg);
}
- 服务回调的 aidl
// ICallbackTestCallback.aidl
package com.callback;
// Declare any non-default types here with import statements
interface ICallbackTestCallback {
/**
* 服务端调用客户端的回调
**/
void onReceived(String msg);
}
aidl 的文件会自动编译成Binder对象的子类,这个编译系统已经为我们做好了
3. 服务端实现
然后,frameworks/base/services/core/java/com/android/server/CallBackTestService.java
。在这里新建一个文件,用来作为服务端的真正实现
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();
}
}
};
/**
*
* @param msg
*/
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
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 里面,修改一下这个文件
diff --git a/build/make/core/tasks/check_boot_jars/package_whitelist.txt b/build/make/core/tasks/check_boot_jars/package_whitelist.txt
index 38f2be57af..883b6d86ff 100644
--- a/build/make/core/tasks/check_boot_jars/package_whitelist.txt
+++ b/build/make/core/tasks/check_boot_jars/package_whitelist.txt
@@ -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 文件来添加关于这个新增加的服务的规则
diff --git a/system/sepolicy/prebuilts/api/29.0/private/service.te b/system/sepolicy/prebuilts/api/29.0/private/service.te
index a8ee195590..7d9a2452fa 100644
--- a/system/sepolicy/prebuilts/api/29.0/private/service.te
+++ b/system/sepolicy/prebuilts/api/29.0/private/service.te
@@ -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;
diff --git a/system/sepolicy/prebuilts/api/29.0/private/service_contexts b/system/sepolicy/prebuilts/api/29.0/private/service_contexts
index 96d553bf49..9067330ae5 100644
--- a/system/sepolicy/prebuilts/api/29.0/private/service_contexts
+++ b/system/sepolicy/prebuilts/api/29.0/private/service_contexts
@@ -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
diff --git a/system/sepolicy/prebuilts/api/29.0/private/untrusted_app_all.te b/system/sepolicy/prebuilts/api/29.0/private/untrusted_app_all.te
index 3c20c082b7..d8e2fac17d 100644
--- a/system/sepolicy/prebuilts/api/29.0/private/untrusted_app_all.te
+++ b/system/sepolicy/prebuilts/api/29.0/private/untrusted_app_all.te
@@ -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)
diff --git a/system/sepolicy/private/service.te b/system/sepolicy/private/service.te
index a8ee195590..7d9a2452fa 100644
--- a/system/sepolicy/private/service.te
+++ b/system/sepolicy/private/service.te
@@ -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;
diff --git a/system/sepolicy/private/service_contexts b/system/sepolicy/private/service_contexts
index 96d553bf49..9067330ae5 100644
--- a/system/sepolicy/private/service_contexts
+++ b/system/sepolicy/private/service_contexts
@@ -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
diff --git a/system/sepolicy/private/untrusted_app_all.te b/system/sepolicy/private/untrusted_app_all.te
index 3c20c082b7..d8e2fac17d 100644
--- a/system/sepolicy/private/untrusted_app_all.te
+++ b/system/sepolicy/private/untrusted_app_all.te
@@ -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 && mae
之后,就可以启动虚拟机了。
然后再新建一个应用。
.
├── 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的内容如下
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");
}
}
}
对应的资源文件如下
<?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生成
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文件有关的内容
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
这个文件,修改内容如下
diff --git a/frameworks/base/Android.bp b/frameworks/base/Android.bp
index 663354835e..03a98395e5 100644
--- a/frameworks/base/Android.bp
+++ b/frameworks/base/Android.bp
@@ -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 来使用的。