当前位置: 首页 > news >正文

Android 多进程开发 - AIDL 回调、RemoteCallbackList、AIDL 安全校验

一、AIDL 的回调

1、基本介绍
  • AIDL 的回调指服务端主动通知客户端,例如,位置变化,这需要跨进程回调
2、演示
(1)Callback
  • IPlayerCallback.aidl,这里是位于src/main/java/com/my/common包下
packagecom.my.common;interfaceIPlayerCallback{voidonSongChanged(StringsongName);voidonPlayStateChanged(booleanisPlaying);}
(2)AIDL
  • IMyAidlInterface.aidl,这里是位于src/main/java/com/my/common包下
packagecom.my.common;importcom.my.common.IPlayerCallback;interfaceIMyAidlInterface{voidplay();voidpause();voidregisterCallback(IPlayerCallbackcallback);voidunregisterCallback();}
(3)Server
privateIPlayerCallbackcallback;privateStringsongName;privatebooleanisPlaying=false;privatefinalIMyAidlInterface.Stubbinder=newIMyAidlInterface.Stub(){@Overridepublicvoidplay()throwsRemoteException{if(isPlaying)return;isPlaying=true;if(callback==null)return;callback.onPlayStateChanged(isPlaying);if(songName==null){songName="Server Song";callback.onSongChanged(songName);}}@Overridepublicvoidpause()throwsRemoteException{if(!isPlaying)return;isPlaying=false;if(callback==null)return;callback.onPlayStateChanged(isPlaying);}@OverridepublicvoidregisterCallback(IPlayerCallbackcallback)throwsRemoteException{ServerService.this.callback=callback;}@OverridepublicvoidunregisterCallback()throwsRemoteException{ServerService.this.callback=null;}};
(4)Client
try{myAidlInterface.play();Log.i(TAG,"play method success");}catch(RemoteExceptione){e.printStackTrace();Log.e(TAG,"play method error: "+e.getMessage());}
try{myAidlInterface.pause();Log.i(TAG,"pause method success");}catch(RemoteExceptione){e.printStackTrace();Log.e(TAG,"pause method error: "+e.getMessage());}
try{myAidlInterface.registerCallback(newIPlayerCallback.Stub(){@OverridepublicvoidonSongChanged(StringsongName)throwsRemoteException{Log.i(TAG,"now thread: "+Thread.currentThread().getName());Log.i(TAG,"onSongChanged: "+songName);}@OverridepublicvoidonPlayStateChanged(booleanisPlaying)throwsRemoteException{Log.i(TAG,"now thread: "+Thread.currentThread().getName());Log.i(TAG,"onPlayStateChanged: "+isPlaying);}});Log.i(TAG,"registerCallback method success");}catch(RemoteExceptione){e.printStackTrace();Log.e(TAG,"registerCallback method error: "+e.getMessage());}
try{myAidlInterface.unregisterCallback();Log.i(TAG,"unregisterCallback method success");}catch(RemoteExceptione){e.printStackTrace();Log.e(TAG,"unregisterCallback method error: "+e.getMessage());}
(5)Test
  1. 先调用 registerCallback 方法,输出结果如下
registerCallback method success
  1. 再调用 play 方法,输出结果如下
now thread: main onPlayStateChanged: true now thread: main onSongChanged: Server Song play method success
  1. 再调用 pause 方法,输出结果如下
now thread: main onPlayStateChanged: false pause method success
  1. 最后调用 unregisterCallback 方法,输出结果如下
unregisterCallback method success

二、RemoteCallbackList

1、基本介绍
  1. RemoteCallbackList 是 Android 专门为跨进程通信设计的一个工具类,它用于安全、自动地管理回调

  2. beginBroadcast 是 RemoteCallbackList 中用于安全遍历列表的一个方法,它配合 finishBroadcast 方法一起使用

  3. beginBroadcast 方法会固定当前要遍历的快照(记录当前所有存活回调的数量),在调用finishBroadcast 方法之前,列表不会变化

2、演示
(1)Callback
  • IPlayerCallback.aidl,这里是位于src/main/java/com/my/common包下
packagecom.my.common;interfaceIPlayerCallback{voidonSongChanged(StringsongName);voidonPlayStateChanged(booleanisPlaying);}
(2)AIDL
  • IMyAidlInterface.aidl,这里是位于src/main/java/com/my/common包下
packagecom.my.common;importcom.my.common.IPlayerCallback;interfaceIMyAidlInterface{voidplay();voidpause();voidregisterCallback(IPlayerCallbackcallback);voidunregisterCallback(IPlayerCallbackcallback);}
(3)Server
privateRemoteCallbackList<IPlayerCallback>callbackList=newRemoteCallbackList<>();privateStringsongName;privatebooleanisPlaying=false;privatefinalIMyAidlInterface.Stubbinder=newIMyAidlInterface.Stub(){@Overridepublicvoidplay()throwsRemoteException{if(isPlaying)return;isPlaying=true;booleanisSongNameChanged=false;if(songName==null){songName="Server Song";isSongNameChanged=true;}intn=callbackList.beginBroadcast();for(inti=0;i<n;i++){IPlayerCallbackcallback=callbackList.getBroadcastItem(i);callback.onPlayStateChanged(isPlaying);if(isSongNameChanged)callback.onSongChanged(songName);}callbackList.finishBroadcast();}@Overridepublicvoidpause()throwsRemoteException{if(!isPlaying)return;isPlaying=false;intn=callbackList.beginBroadcast();for(inti=0;i<n;i++){IPlayerCallbackcallback=callbackList.getBroadcastItem(i);callback.onPlayStateChanged(isPlaying);}callbackList.finishBroadcast();}@OverridepublicvoidregisterCallback(IPlayerCallbackcallback)throwsRemoteException{callbackList.register(callback);}@OverridepublicvoidunregisterCallback(IPlayerCallbackcallback)throwsRemoteException{callbackList.unregister(callback);}};
(4)Client
try{myAidlInterface.play();Log.i(TAG,"play method success");}catch(RemoteExceptione){e.printStackTrace();Log.e(TAG,"play method error: "+e.getMessage());}
try{myAidlInterface.pause();Log.i(TAG,"pause method success");}catch(RemoteExceptione){e.printStackTrace();Log.e(TAG,"pause method error: "+e.getMessage());}
try{myAidlInterface.registerCallback(newIPlayerCallback.Stub(){@OverridepublicvoidonSongChanged(StringsongName)throwsRemoteException{Log.i(TAG,"now thread: "+Thread.currentThread().getName());Log.i(TAG,"onSongChanged: "+songName);}@OverridepublicvoidonPlayStateChanged(booleanisPlaying)throwsRemoteException{Log.i(TAG,"now thread: "+Thread.currentThread().getName());Log.i(TAG,"onPlayStateChanged: "+isPlaying);}});Log.i(TAG,"registerCallback method success");}catch(RemoteExceptione){e.printStackTrace();Log.e(TAG,"registerCallback method error: "+e.getMessage());}
try{myAidlInterface.unregisterCallback();Log.i(TAG,"unregisterCallback method success");}catch(RemoteExceptione){e.printStackTrace();Log.e(TAG,"unregisterCallback method error: "+e.getMessage());}
(5)Test
  1. 先调用 registerCallback 方法,输出结果如下
registerCallback method success
  1. 再调用 play 方法,输出结果如下
now thread: main onPlayStateChanged: true now thread: main onSongChanged: Server Song play method success
  1. 再调用 pause 方法,输出结果如下
now thread: main onPlayStateChanged: false pause method success
  1. 最后调用 unregisterCallback 方法,输出结果如下
unregisterCallback method success

三、AIDL 安全校验

需求引入
  • 默认情况下,任何知道 AIDL 接口的应用都能绑定服务并调用方法,这可能导致数据泄露和恶意调用
1、权限校验
(1)Server
  1. AndroidManifest.xml,定义权限
<permissionandroid:name="com.my.ACCESS_TEST_SERVICE"android:protectionLevel="normal"/>
  1. 在 Binder 方法中校验权限
intresult=checkCallingPermission("com.my.ACCESS_TEST_SERVICE");Log.i(TAG,"权限检查结果: "+result);if(result==PackageManager.PERMISSION_DENIED){Log.i(TAG,"权限拒绝");thrownewSecurityException("权限拒绝");}Log.i(TAG,"权限通过");// 执行其他业务逻辑
(2)Client
  • AndroidManifest.xml,声明权限
<uses-permissionandroid:name="com.my.ACCESS_TEST_SERVICE"/>
2、UID / PID 校验
  • Server:在 Binder 方法中校验 UID / PID
intcallingUid=Binder.getCallingUid();if(callingUid!=1000&&callingUid!=1001){thrownewSecurityException("无权调用");}// 执行其他业务逻辑
3、包名白名单校验
  • Server:在 Binder 方法中校验包名白名单
privatestaticfinalList<String>ALLOWED_PACKAGES=Arrays.asList("com.my.server","com.my.client");privatebooleanallowedPackagesCheck(){intcallingUid=Binder.getCallingUid();String[]packages=getPackageManager().getPackagesForUid(callingUid);if(packages!=null){for(Stringpkg:packages){Log.i(TAG,"package: "+pkg);if(ALLOWED_PACKAGES.contains(pkg)){returntrue;}}}returnfalse;}
if(!allowedPackagesCheck()){Log.i(TAG,"调用者未被授权");thrownewSecurityException("调用者未被授权");}Log.i(TAG,"调用者已被授权");// 执行其他业务逻辑

四、protectionLevel(补充学习)

  • protectionLevel 是 Android 权限系统中定义权限风险等级的属性
  1. normal:普通级别,安装时自动授予,无需用户确认

  2. dangerous:危险级别,运行时需用户明确授权

  3. signature:签名级别,只有相同签名的应用才能获得权限,安装时自动授予

  4. signatureOrSystem:签名或系统级别,只有相同签名的应用才能获得权限,或者位于 Android 系统映像的专用文件夹中的应用才能获得权限,在 API 级别 23 中已废弃,是signature|privileged的旧同义词,建议使用 signature 代替

  • 参考文档 1:https://developer.android.google.cn/guide/topics/manifest/permission-element?hl=zh-cn

  • 参考文档 2:https://developer.android.google.cn/reference/android/R.attr#protectionLevel

http://www.jsqmd.com/news/409948/

相关文章:

  • 为什么 Controller 层坚决不能直接调 DAO 层?
  • Redis 的 ZipList 是什么?它是怎么解决内存碎片问题的?
  • 小遥搜索v1.2.0版本更新【已支持-语雀数据源集成】
  • 梦笔记20260225
  • 2026年智能系统门窗公司综合评估:五大品牌实力对比 - 2026年企业推荐榜
  • 2026年河南氧系漂白剂直销公司综合评估与精选推荐 - 2026年企业推荐榜
  • 2026年静音系统门窗诚信服务商综合评测与选购指南 - 2026年企业推荐榜
  • 2026年河南道闸广告平台深度盘点与选择指南 - 2026年企业推荐榜
  • 2026年实力MBBR填料厂商盘点与选型指南 - 2026年企业推荐榜
  • 移动硬盘被system占用无法弹出
  • 2026年河南过氧碳酸钠采购指南:五大优质供应商深度解析 - 2026年企业推荐榜
  • 2026年河南灯光秀广告公司选购指南:实力品牌深度解析 - 2026年企业推荐榜
  • 2026年江西全屋高端木作品牌深度评测 - 2026年企业推荐榜
  • 基于SpringBoot+Vue的和智慧生活商城系统管理系统设计与实现【Java+MySQL+MyBatis完整源码】
  • 成都地区官网搭建公司选择要点 - 2026年企业推荐榜
  • 2026北京厨房家装门窗直销厂商综合评测与选型指南 - 2026年企业推荐榜
  • ARM逆向工程权威公开资料
  • 2026年濮阳过碳酸钠服务商选择指南与优质厂商推荐 - 2026年企业推荐榜
  • 2026年初成都视频号推广服务商选购权威指南 - 2026年企业推荐榜
  • 东南亚海外仓人员管理痛点:本土员工难管?这套海外仓WMS帮你规范化
  • 2026年武汉咸宁名牌箱包回收机构深度测评与选购指南 - 2026年企业推荐榜
  • 东南亚海外仓盈利关键:用海外仓WMS解决多货主货权纠纷、库存不准
  • 2026年Q1安徽网店代运营机构专业度测评与选购指南 - 2026年企业推荐榜
  • 从感知到部署:构建下一代高鲁棒性图像分类组件的深度实践
  • 2026年湖北专精特新申报服务公司综合观察 - 2026年企业推荐榜
  • [特殊字符] AI-DNA 思考引擎内核 v1.0 · 创新发布日志
  • 2026年派遣翻译服务团队选购指南与TOP5推荐 - 2026年企业推荐榜
  • 2026年阜阳专业软床家具厂家盘点与选择建议 - 2026年企业推荐榜
  • 2026年宜兴金属锥体选购指南:技术要点与优质厂家解析 - 2026年企业推荐榜
  • 2026年Q1专业派驻翻译服务商综合实力测评与选购指南 - 2026年企业推荐榜