0%

安卓 RemoteCallbackList 的使用 (系统服务篇)

接着应用篇继续说,这次我们在系统服务里面使用 RemoteCallbackList 。
首先,你需要有一份能完整编译的安卓源码。

我这里以 Android10_r47为例。
我在 frameworks/base/core/java/com/callback/ 内新增这几个文件。

  1. 服务接口的 aidl
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 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);
    }
  2. 服务回调的 aidl
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 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。在这里新建一个文件,用来作为服务端的真正实现
    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();
    }
    }
    };

    /**
    *
    * @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
    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
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 文件来添加关于这个新增加的服务的规则

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
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 && 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
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之后,普通应用就可以像调用安卓已有的服务那样,来使用新增的这个服务了。

复习一下

有几个小地方需要注意:

  1. 如果你添加的接口没有出现在 current.txt 里面的话,安卓10上由于谷歌新增的hidden api访问限制,普通应用是无法访问的
  2. 使用AOSP 编译出来的 signapk.jar 和 platform 签名的apk,似乎无法在虚拟机上安装为系统应用,我是把 platform key 转换成 Android Studio 使用的 keystore 来使用的。