继续修改demo,加一些校验。
AndroidManifest中定义权限:

remoteService挂载权限:

RemoteService 里的 add(int a, int b) ,在业务代码之前插入权限检查:

运行:

缺少权限声明,看来同一个app的不同进程之间调用也不能缺少权限声明,加上:

再测试:

嗯 这次没报错。
再修改下demo,对调用方包名做白名单校验。
在RemoteService 的 IMyAidlInterface.Stub 里,这样校验白名单写:

然后在每个 AIDL 方法的第一行调用它:

测试:

可以看到,能获取到调用方包名。ok.
如果不想每个aidl方法都加校验,也可以在Stub 的 onTransact方法中统一拦截,这里:

ok. 完整代码。RemoteService:
java
package com.example.binderdemo;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class RemoteService extends Service {
private static final String TAG = "RemoteService";
// private final List<ICallback> callbacks = new ArrayList<>();
private final android.os.RemoteCallbackList<ICallback> callbacks =
new android.os.RemoteCallbackList<>();
private final android.os.Handler handler =
new android.os.Handler(android.os.Looper.getMainLooper());
private final Runnable tick = new Runnable() {
@Override
public void run() {
int n = callbacks.beginBroadcast();
Log.d(TAG, "tick, callbacks.size=" + n);
Log.d(TAG, "tick, callbacks.size=" + n + ", 何以解忧,唯有退休?");
try {
for (int i = 0; i < n; i++) {
ICallback cb = callbacks.getBroadcastItem(i);
try {
cb.onResult(9527);
} catch (RemoteException e) {
Log.e(TAG, "tick onResult failed: " + e);
}
}
} finally {
callbacks.finishBroadcast();
}
handler.postDelayed(this, 10000);
}
};
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate, start tick");
handler.postDelayed(tick, 10000);
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy, stop tick");
handler.removeCallbacks(tick);
super.onDestroy();
}
private void broadcastResult(int result) {
int n = callbacks.beginBroadcast();
try {
for (int i = 0; i < n; i++) {
ICallback cb = callbacks.getBroadcastItem(i);
try {
cb.onResult(result);
} catch (RemoteException e) {
Log.e(TAG, "onResult failed: " + e);
}
}
} finally {
callbacks.finishBroadcast();
}
}
private static final Set<String> ALLOWED_PACKAGES =
new HashSet<>(Arrays.asList("com.example.binderdemo"));
private final IMyAidlInterface.Stub binder = new IMyAidlInterface.Stub() {
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
// 也可以在这里统一拦截。
// if (!isCallerAllowed()) {
// return false; // 或不返回结果,让调用方收到异常
// }
return super.onTransact(code, data, reply, flags);
}
private boolean isCallerAllowed() {
int callingUid = Binder.getCallingUid();
// uid 0 是 root,视场景决定是否放行
if (callingUid == 0) return false;
String[] packages = getPackageManager().getPackagesForUid(callingUid);
if (packages == null) return false;
Log.d(TAG, "packages: " + Arrays.toString(packages));
for (String pkg : packages) {
if (ALLOWED_PACKAGES.contains(pkg)) {
return true;
}
}
return false;
}
@Override
public int add(int a, int b) throws RemoteException {
if (!isCallerAllowed()) {
throw new SecurityException(
"Caller uid=" + Binder.getCallingUid() + " is not whitelisted");
}
// 关键:在执行逻辑之前,检查调用方是否持有权限
if (checkCallingPermission("com.example.binderdemo.ACCESS_REMOTE_SERVICE")
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException(
"Caller pid=" + Binder.getCallingPid()
+ " uid=" + Binder.getCallingUid()
+ " lacks ACCESS_REMOTE_SERVICE. 总舵主:熙官,别来无恙。");
}
Log.d(TAG, "add(" + a + "," + b + ") START thread=" + Thread.currentThread().getName()
+ ", pid=" + Process.myPid());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, "add(" + a + "," + b + ") END thread=" + Thread.currentThread().getName());
int result = a + b;
// 不在 Binder 线程里广播,post 到主线程统一做
handler.post(() -> broadcastResult(result));
return result;
}
@Override
public String getMessage() {
if (!isCallerAllowed()) {
throw new SecurityException(
"Caller uid=" + Binder.getCallingUid() + " is not whitelisted");
}
Log.d(TAG, "getMessage thread="
+ Thread.currentThread().getName()
+ ", pid=" + Process.myPid());
return "Hello from pid=" + Process.myPid() + " , 为人不识陈近南,纵称英雄也枉然。";
}
@Override
public void registerCallback(ICallback cb) throws RemoteException {
if (!isCallerAllowed()) {
throw new SecurityException(
"Caller uid=" + Binder.getCallingUid() + " is not whitelisted");
}
if (cb == null) return;
Log.d(TAG, "registerCallback cb=" + cb
+ ", class=" + cb.asBinder().getClass().getName()
+ ", thread=" + Thread.currentThread().getName());
callbacks.register(cb);
}
@Override
public void unregisterCallback(ICallback cb) throws RemoteException {
if (!isCallerAllowed()) {
throw new SecurityException(
"Caller uid=" + Binder.getCallingUid() + " is not whitelisted");
}
if (cb != null) {
callbacks.unregister(cb);
}
}
};
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind thread="
+ Thread.currentThread().getName()
+ ", pid=" + Process.myPid());
return binder;
}
}
AndroidManifest:
XML
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- 定义自定义权限:signature 级别最严格,只允许同签名应用持有 -->
<permission
android:name="com.example.binderdemo.ACCESS_REMOTE_SERVICE"
android:protectionLevel="signature" />
<uses-permission android:name="com.example.binderdemo.ACCESS_REMOTE_SERVICE" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Binderdemo">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.Binderdemo">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".RemoteService"
android:process=":remote"
android:exported="true"
android:permission="com.example.binderdemo.ACCESS_REMOTE_SERVICE"/>
<service android:name=".RemoteService2"
android:process=":remote"
android:exported="false"/>
</application>
</manifest>
MainActivity:
java
package com.example.binderdemo;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
import android.widget.TextView;
import androidx.activity.ComponentActivity;
import androidx.annotation.Nullable;
import static com.example.binderdemo.IMyAidlInterface.DESCRIPTOR;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MainActivity extends ComponentActivity {
private static final String TAG = "ClientMainActivity";
private IMyAidlInterface remote;
private final ICallback callback = new ICallback.Stub() {
@Override
public void onResult(int result) {
Log.d(TAG, "onResult result=" + result
+ ", thread=" + Thread.currentThread().getName() // binder线程
+ ", pid=" + Process.myPid());
runOnUiThread(() -> tv.setText("callback result=" + result));
}
};
//private IMiniInterface remote;
private TextView tv;
private boolean bound;
private final ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected thread="
+ Thread.currentThread().getName());
// // 这个service类型是android.os.BinderProxy,远端代理,用于跨进程通信。
// // 这里asInterface返回一个客户端Proxy对象,里面的成员变量mRemote就是这个service对象。
// remote = IMyAidlInterface.Stub.asInterface(service);
// 手写版 asInterface:跨进程直接返回 Proxy
// remote = new MiniProxy(service);
// android.os.IInterface iin = service.queryLocalInterface(DESCRIPTOR); // 跨进程通信时,搜索为null
// Log.d(TAG, "iin=" + iin); // iin=null
remote = IMyAidlInterface.Stub.asInterface(service);
try {
// 先注册callback
remote.registerCallback(callback);
Log.d(TAG, "registered callback, callback.asBinder()="
+ callback.asBinder().getClass().getName());
// 然后调用add方法
int result = remote.add(1, 2);
// String msg = remote.getMessage();
// Log.d(TAG, "add result=" + result + ", msg=" + msg);
// tv.setText("result=" + result + "\n" + msg);
tv.setText("add result=" + result);
Log.d(TAG, "当前客户端进程:"+ Process.myPid());
Log.d(TAG, "remote: " + remote);
Log.d(TAG, "service: " + service);
} catch (RemoteException e) {
e.printStackTrace();
}
Log.d(TAG, "client pid=" + Process.myPid());
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected");
remote = null;
}
};
private void concurrentCalls() {
if (remote == null) {
Log.d(TAG, "remote is null, not connected");
return;
}
final int N = 20;
ExecutorService pool = Executors.newFixedThreadPool(N);
final CountDownLatch ready = new CountDownLatch(N);
final CountDownLatch start = new CountDownLatch(1);
final long begin = System.currentTimeMillis();
for (int i = 0; i < N; i++) {
final int idx = i;
pool.execute(() -> {
ready.countDown();
try {
start.await(); // 等所有线程就绪,一起发
Log.d(TAG, "call add #" + idx + " thread="
+ Thread.currentThread().getName());
int r = remote.add(idx, idx);
Log.d(TAG, "call add #" + idx + " result=" + r
+ ", elapsed=" + (System.currentTimeMillis() - begin));
} catch (Exception e) {
Log.d(TAG, "error: " + e);
}
});
}
try {
ready.await();
start.countDown(); // 发令枪
} catch (InterruptedException e) {
Log.d(TAG, "err: " + e);
}
pool.shutdown();
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
tv = new TextView(this);
// 加一个按钮,点击杀掉自己进程
android.widget.Button killBtn = new android.widget.Button(this);
killBtn.setText("杀掉客户端进程");
killBtn.setOnClickListener(v -> {
Log.d(TAG, "kill self, pid=" + Process.myPid());
android.os.Process.killProcess(android.os.Process.myPid());
});
android.widget.LinearLayout root = new android.widget.LinearLayout(this);
root.setOrientation(android.widget.LinearLayout.VERTICAL);
root.addView(tv);
root.addView(killBtn);
setContentView(root);
android.widget.Button concurrentBtn = new android.widget.Button(this);
concurrentBtn.setText("并发调用 add x20");
concurrentBtn.setOnClickListener(v -> concurrentCalls());
root.addView(concurrentBtn);
Intent intent = new Intent(this, RemoteService.class);
// Intent intent = new Intent(this, RemoteService2.class);
startService(intent);
bound = bindService(intent, conn, BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
if (bound) {
try {
if (remote != null) {
remote.unregisterCallback(callback);
}
} catch (RemoteException e) {
e.printStackTrace();
}
unbindService(conn);
bound = false;
}
super.onDestroy();
}
}
ok.