需求场景
当上层app需要调用一些系统底层的资源以及属性操作(比如Settings.system 属性的读写),甚至驱动节点的读写操作,上层app毫无疑问是没有权限的,所以就需要我们在framework 系统层做一个中转和代理,也就是做一个远程服务(AIDL实现,开放远程调用接口)。这种实现一般有两种思路
- framework里实现一个系统级服务,开机自启,注册到系统服务的启动流程里,这种思路,针对像对写敏感,加了系统级进程用户组的检查的话,比如Settings.system的写操作,还是会被拦下,故不再赘述。
- 实现一个系统级的app,包装一个服务,封装好逻辑后,放到系统源码里参与编译即可。实测可行,并且由于是做app,在AS中直接开发,有语法提示高亮错误提醒,开发相当友好!
所以下面就讲解实现系统级APP包装远程服务的具体实现的步骤,主要就是围绕AIDL的实现
> 服务端
-
AIDL服务声明
AS 直接在app 主module 上右键,new -> AIDL -> AIDL File
例如 新建 ITestService.aidl
AS 会自动完成AIDL的中间类的创建,项目下新建aild 目录,与java同级,包名路径也保持一致package com.xxx.server; interface ITestService { int getScreenBrightness(); boolean setScreenBrightness(int brightness); }
这里我们实现一个 控制屏幕亮度的设置,针对系统Settings属性的读写操作
-
Java服务实现
继承Service ,实现onBind(Intent intent) ,返回IBinder对象。
实现ITestService.Stub(),即ITestService AIDL接口的具体实现
重写实现ITestService接口声明的方法package com.xxx.server; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.provider.Settings; import androidx.annotation.Nullable; public class xxxManagerService extends Service{ @Nullable @Override public IBinder onBind(Intent intent) { return testStub; } private final ITestService.Stub testStub = new ITestService.Stub() { @Override public int getScreenBrightness() throws RemoteException { int result = -1; try { result = Settings.System.getInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS); } catch (Settings.SettingNotFoundException e) { e.printStackTrace(); } return result; } @Override public boolean setScreenBrightness(int brightness) throws RemoteException { boolean optResult; int resultBrightness = 102;// default brightness 102 40% if (brightness <= 255 && brightness > 10) { resultBrightness = brightness; } // 自动亮度改为手动亮度 try { int mode = Settings.System.getInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE); if (mode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC) { Settings.System.putInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL); } } catch (Settings.SettingNotFoundException e) { e.printStackTrace(); } optResult = Settings.System.putInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, resultBrightness); return optResult; } } }
-
AndroidManifest.xml的配置
对于只是包装一个服务,我们不再需要UI界面,不在需要它像普通app那样安装显示在桌面上
- manifest根节点添加android:sharedUserId=“android.uid.system” 属性,即让该app加入系统进程组,即变成系统级权限app,拥有对系统级限制的操作有了权限
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.xxx.server" android:sharedUserId="android.uid.system" xmlns:tools="http://schemas.android.com/tools">
-
去掉activity的注册,同时项目内的activity的实现和layout都可以删除掉了。然后注册我们的主角xxxManagerService
注意: enabled 与 exported 必须true ; action为自定义
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" tools:targetApi="31"> <!-- 去掉Activity的注册,因为不需要UI --> <!-- <activity--> <!-- android:name=".MainActivity"--> <!-- android:exported="true">--> <!-- <intent-filter>--> <!-- <action android:name="android.intent.action.MAIN" />--> <!-- <category android:name="android.intent.category.LAUNCHER" />--> <!-- <category android:name="android.intent.category.DEFAULT" />--> <!-- </intent-filter>--> <!-- </activity>--> <service android:name=".xxxManagerService" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="action.com.xxx.server"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </service> </application>
到这里 ,作为服务端,开放给上层的调用的配置基本就算完整了。但是实际需求,可能还会涉及到 AIDL 传输非基本数据类型的情况,AIDL 里做个对象的声明实现即可,具体不再展开
还有可能会涉及到,服务端主动回调客户端的情况
- 服务端回调客户端
同包名下新建ITestCallback.aidl,声明回调接口的方法
然后在ITestService下面加入注册和解注册的方法package com.xxx.server; interface ITestCallback { void onRemoteCallback(String msg); }
注意: 这里要把回调接口导包进来,尽管他们的包名路径一样
Java 服务接口实现里,加入刚才两个方法的实现import com.xxx.server.ITestCallback; interface ITestService { int getScreenBrightness(); boolean setScreenBrightness(int brightness); // 加入回调接口的注册和解注册 void registerCallBack(ITestCallback callback); void unregisterCallBack(ITestCallback callback); }
CallbackRegisterManager的实现,封装下RemoteCallbackList,主要用来管理AIDL接口回调注册的管理package com.xxx.server; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.provider.Settings; import androidx.annotation.Nullable; public class xxxManagerService extends Service{ @Nullable @Override public IBinder onBind(Intent intent) { return testStub; } private final ITestService.Stub testStub = new ITestService.Stub() { ... 省略其他方法实现 ... @Override public void registerCallBack(ITestCallback callback) throws RemoteException { CallbackRegisterManager.getInstance().register(callback); } @Override public void unregisterCallBack(ITestCallback callback) throws RemoteException { CallbackRegisterManager.getInstance().unRegister(callback); } } }
public class CallbackRegisterManager { private static CallbackRegisterManager instance; private RemoteCallbackList<ITestCallback> remoteCallbackList = null; private CallbackRegisterManager() { remoteCallbackList = new RemoteCallbackList<>(); } public static CallbackRegisterManager getInstance() { synchronized (CallbackRegisterManager.class) { if (instance == null) { instance = new CallbackRegisterManager(); } } return instance; } public RemoteCallbackList<ITestCallback> getRemoteCallbackList(){ return remoteCallbackList; } public void register(ITestCallback callback){ if(remoteCallbackList != null){ remoteCallbackList.register(callback); } } public void unRegister(ITestCallback callback){ if(remoteCallbackList != null){ remoteCallbackList.unregister(callback); } } }
- 放在系统下编译
首先把上面编写好的app源码打包成apk
AS里Build Variants 选 release ,然后 Build -> Build Bundle(s) / APK(s) -> Build APK(s) ,打好的未签名的包在 build 目录 outputs 中,拷贝出apk 文件,修改自定义的名字 ,例如TestServer.apk
在自己源码三方app或者系统app编译的地方,新建脚本LOCAL_MODULE同名文件夹 /TestServer ,然后将如下内容 Android.mk 脚本和TestServer.apk一起放入
让这个新增的app参与系统编译,不同平台略有不同,不在赘述。LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := TestServer LOCAL_MODULE_CLASS := APPS LOCAL_MODULE_TAGS := optional LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX) LOCAL_CERTIFICATE := platform LOCAL_SRC_FILES := $(LOCAL_MODULE).apk LOCAL_SYSTEM_EXT_MODULE := true LOCAL_PRIVILEGED_MODULE := true include $(BUILD_PREBUILT)
> 客户端
由于提供三方app使用,需要将客户端包装成 Library,所以新建 项目 选择 Android Library类型
将服务端的AIDL 原封不动的拷贝到 项目里 ,即 java同级目录aidl目录下
注意: 包名路径和内部方法名称入参等需要和服务端完全一致,不然后面调用就会出现问题,所以直接拷贝过来,简单安全
-
开放给外部的管理器的实现
public class TestManager { private Context mContext; private static TestManager mInstance; private ITestService mService; private IBinder b; private boolean connected; private TestCallbackImpl callbackImpl; private ICustomCallback icustomCallback; private TestManager(Context context, ICustomCallback iCallback) { mContext = context.getApplicationContext(); this.icustomCallback = iCallback; // 链接AIDL远程服务 bindRemoteService(conn); } private void bindRemoteService(ServiceConnection serviceConnection) { Intent intent = new Intent(); // 指定包名 ,指向我们那个前面做服务的做进系统服务的app的包名 intent.setPackage("com.xxx.server"); // // 这里的action 即 为 前面我们注册服务自定义的action intent.setAction("action.com.xxx.server"); mContext.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); } // 注册获取管理对象,同时传入三方客户调用端接收远程服务回调的接口 public static synchronized TestManager register(@NonNull Context context, ICustomCallback icustomCallback) { if (mInstance == null) { mInstance = new TestManager(context, iCallback); } return mInstance; } public void unRegister() { if (connected) { try { if (callbackImpl != null) { getService().unregisterCallBack(callbackImpl); } } catch (Exception e) { e.printStackTrace(); } mContext.unbindService(conn); } } // 获取服务对象 private ITestService getService() { if (mService != null) { return mService; } mService = ITestService.Stub.asInterface(b); return mService; } private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { b = iBinder; connected = true; // 初始化aidl远程回调 callbackImpl = new TestCallbackImpl(icustomCallback); // 注册aidl远程回调 try { getService().registerCallBack(callbackImpl); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName componentName) { connected = false; } }; // 这里可以做成扔出自定义异常,提醒调用端 private void isServiceConnected(String opt) { if (!connected) { Log.e(TAG, "opt: " + opt + " -- service not connected!"); } } // 下面便是对上层直接开放的方法 // public int getScreenBrightness() { isServiceConnected("getScreenBrightness"); int result = -1; try { result = getService().getScreenBrightness(); } catch (RemoteException e) { e.printStackTrace(); } return result; } public boolean setScreenBrightness(int brightness) { isServiceConnected("setScreenBrightness"); boolean optResult = true; try { optResult = getService().setScreenBrightness(brightness); } catch (RemoteException e) { optResult = false; e.printStackTrace(); } return optResult; } }
下面是服务端回调接口 ITestCallback 的实现,入参 ICustomCallback为三方app调用端去实现的接收服务回调数据的接口
public class TestCallbackImpl extends ITestCallback.Stub { private ICustomCallback ICustomCallback = null; public TestCallbackImpl(ICustomCallback ICustomCallback) { this.iCallback = iCustomCallback; } @Override public void onRemoteCallback(String msg) throws RemoteException { if (ICustomCallback != null) { ICustomCallback.onCustomCallback(msg); } } }
客户端ICustomCallback回调的接口实现,也很简单
public interface ICustomCallback { void onCustomCallback(String msg); }
至此,这个提供给上层app 使用的封装操作的Library module项目基本已经完成,然后将它编译打包,获取他的aar库包,给到任何客户端即可使用
- 三方app端调用
private TestManager testManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); testManager = TestManager.register(this, myCustomCallback); } @Override protected void onDestroy() { super.onDestroy(); if (testManager != null) { testManager.unRegister(); } } ICustomCallback myCustomCallback = new ICustomCallback() { @Override public void onCustomCallback(String msg) { // 做相应操作 } };