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

Android 11添加系统服务,并封装jar包供第三方应用使用

概述:
如果你是做技术支持,有没有遇到这种情况,客户既要实现具备系统权限的功能,但是呢,又不想把自己的应用做成系统应用。这时候你咋办。
我们可以添加一个具备系统权限的服务,不管前台的,还是后台的,都可以。今天的栗子,我们就用前台服务,为啥用前台服务了,因为状态栏我可以很清楚的看台它在运行。实现原理,还是通过进程通信,AIDL实现。我们可以将服务端内置到aosp中,或者直接将服务端源码内置aosp中,下面我们就直接打包成apk,然后内置,客户端,我们可以直接打包成jar包,封装需要系统权限的接口,供二次开发。
先介绍一下,我们整个项目的目录。app模块是我们内置的服务端,SystemToolService是打包的客户端,JarTest就是我们最终测试jar包的简单demo。
在这里插入图片描述

一、编写服务端

  1. 我们先创建个项目,配置为系统权限android:sharedUserId=“android.uid.system”,并添加系统签名。

  2. 创建服务,并添加一个自定义服务,其他部分和前面Android进程通信AIDL相同,添加服务部如下:

SystemToolServiceAidl mBinder;public static final String SYSTEM_SERVICE = "systemtool_service";private void publish() {try {mBinder = new SystemToolServiceAidl(this);Class serviceManager = Class.forName("android.os.ServiceManager");Method method = serviceManager.getMethod("addService", String.class, IBinder.class);method.invoke(serviceManager.newInstance(), SYSTEM_SERVICE,mBinder);Log.d(TAG, "publish iSystemService as systemService !");} catch (Exception e) {Log.e(TAG, "publish systemService as systemService Exception!");e.printStackTrace();}}

完整代码如下:

package com.uniriho.androidipc_systemtoolservice.aidl;import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;import androidx.annotation.Nullable;import com.uniriho.androidipc_systemtoolservice.R;import java.lang.reflect.Method;
import java.util.List;public class SystemToolService extends Service {private static final String TAG = "SystemToolService";NotificationManager notificationManager;String notificationId = "channelId";String notificationName = "channelName";private static ActivityManager mActivityManager = null;SystemToolServiceAidl mBinder;public static final String SYSTEM_SERVICE = "systemtool_service";private void publish() {try {mBinder = new SystemToolServiceAidl(this);Class serviceManager = Class.forName("android.os.ServiceManager");Method method = serviceManager.getMethod("addService", String.class, IBinder.class);method.invoke(serviceManager.newInstance(), SYSTEM_SERVICE,mBinder);Log.d(TAG, "publish iSystemService as systemService !");} catch (Exception e) {Log.e(TAG, "publish systemService as systemService Exception!");e.printStackTrace();}}/*** 前台服务*/private void startForegroundService() {notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);//创建NotificationChannelif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {NotificationChannel channel = new NotificationChannel(notificationId, notificationName, NotificationManager.IMPORTANCE_HIGH);notificationManager.createNotificationChannel(channel);}startForeground(1, getNotification());}private Notification getNotification() {Notification.Builder builder = new Notification.Builder(this).setSmallIcon(R.drawable.ic_launcher_background).setContentTitle("SystemToolService").setContentText("SystemTool服务正在运行...");if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {builder.setChannelId(notificationId);}Notification notification = builder.build();return notification;}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.d(TAG, "SystemToolService onStartCommand");Log.d(TAG, "AIDLService isServiceAlive==" + isServiceAlive(this));publish();return START_STICKY;}@Overridepublic void onCreate() {Log.d(TAG, "SystemToolService onCreate");super.onCreate();publish();startForegroundService();}private boolean isServiceAlive(Context context) {if (mActivityManager == null) {mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);}List<ActivityManager.RunningServiceInfo> infos = mActivityManager.getRunningServices(Integer.MAX_VALUE);if (infos == null) {return false;} else {if (infos.size() <= 0) {return false;} else {for (ActivityManager.RunningServiceInfo info : infos) {if ("com.uniriho.androidipc_systemtoolservice.aidl.SystemToolService".equals(info.service.getClassName())) {return true;}}}}return false;}@Nullable@Overridepublic IBinder onBind(Intent intent) {return mBinder;}
}
  • 先配置接收开机广播、然后启动服务,这里也可以在开机广播启动应用,将应用设置为无界面应用,这里我们启动一个前台服务。关于未启动应用无法接受开机广播可以参考这部分内容。
//添加权限:<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
//注册广播<receiverandroid:name=".StartReceiver"android:enabled="true"android:exported="true"><intent-filter><action android:name="android.intent.action.BOOT_COMPLETED"/></intent-filter></receiver>
//接收广播,并启动服务
package com.uniriho.androidipc_systemtoolservice;import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;import androidx.annotation.RequiresApi;import com.uniriho.androidipc_systemtoolservice.aidl.SystemToolService;import java.util.List;public class StartReceiver extends BroadcastReceiver {private static final String TAG = "SystemToolService";private static ActivityManager mActivityManager = null;static final String ACTION = "android.intent.action.BOOT_COMPLETED";@RequiresApi(api = Build.VERSION_CODES.O)@Overridepublic void onReceive(Context context, Intent intent) {if (intent.getAction().equals(ACTION)) {if (!isServiceAlive(context)) {Log.d(TAG, "SystemToolService isServiceAlive false");Intent startIntent = new Intent(context, SystemToolService.class);context.startForegroundService(startIntent);}Log.d(TAG, "SystemToolService MyApplication onCreate");}if(intent.getAction().equals("WALLPAPER_CHANGED")){System.out.println("=============");}}private boolean isServiceAlive(Context context) {if (mActivityManager == null) {mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);}List<ActivityManager.RunningServiceInfo> infos = mActivityManager.getRunningServices(Integer.MAX_VALUE);if (infos == null) {return false;} else {if (infos.size() <= 0) {return false;} else {for (ActivityManager.RunningServiceInfo info : infos) {if ("com.uniriho.androidipc_systemtoolservice.aidl.SystemToolService".equals(info.service.getClassName())) {return true;}}}}return false;}}
  • 定义接口
    1、我们还是一如既往,开启AIDL,不然没法创建AIDL文件,低版本的可忽略这步。
    在这里插入图片描述
    2、创建我们的AIDL接口,这里我们用获取蓝牙Mac地址验证,其他方法请忽略。
    在这里插入图片描述
    3、当然就是实现接口了,我们创建个类,继承自我们定义的接口,注意这里是集成接口的Stub类,然后实现其方法,。如下:
    在这里插入图片描述
    这样,我们的服务端就完成了,完整代码如下:
package com.uniriho.androidipc_systemtoolservice.aidl;import android.bluetooth.BluetoothAdapter;
import android.content.ContentResolver;
import android.content.Context;
import android.os.PowerManager;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.Log;import com.uniriho.androidipc_systemtoolservice.ISystemToolService;import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.lang.reflect.Method;public class SystemToolServiceAidl extends ISystemToolService.Stub {private static final String TAG = "SystemToolServiceManager";private Context mContext;private final static Object lockwriteObject = new Object();private final static Object lockreadObject = new Object();PowerManager powerManager;ContentResolver cr;public SystemToolServiceAidl(Context context){mContext = context;Log.d(TAG, "SystemToolServiceManager init");Log.e(TAG,"MySystemToolService");if (powerManager == null) {powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);}if (cr == null) {cr = context.getContentResolver();}}@Overridepublic void poweroff() throws RemoteException {Log.e(TAG,"poweroff");
//            powerManager.shutdown(false, "", false);try {Runtime.getRuntime().exec(new String[]{"su","-c","reboot -p"});} catch (IOException e) {throw new RuntimeException(e);}}@Overridepublic void reboot() throws RemoteException {Log.e(TAG,"reboot");powerManager.reboot("");}@Overridepublic boolean startAdbServer() throws RemoteException {,,,}@Overridepublic boolean stopAdbServer() throws RemoteException {,,,}@Overridepublic boolean setSettingsPassword(String oldPwd, String newPwd) throws RemoteException {,,,}@Overridepublic boolean resetSettingsPassword() throws RemoteException {,,,}@Overridepublic String getFile(String path) throws RemoteException {,,,}@Overridepublic void putFile(String path, String content) throws RemoteException {,,,}@Overridepublic void setProp(String key, String value) throws RemoteException {,,,}@Overridepublic String getBtMac() throws RemoteException {Log.e(TAG,"getBtMac");try{BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();Field field = bluetoothAdapter.getClass().getDeclaredField("mService");// 参数值为true,禁用访问控制检查field.setAccessible(true);Object bluetoothManagerService = field.get(bluetoothAdapter);if (bluetoothManagerService == null) {return null;}Method method = bluetoothManagerService.getClass().getMethod("getAddress");Object address = method.invoke(bluetoothManagerService);if (address != null && address instanceof String) {return (String) address;} else {return null;}}catch (Exception e){e.printStackTrace();return null;}}private void setProperty(String key, String value) {...}
}

二、客户端部分,也就是打包部分

  1. 创建一个Module,选择Android library
    在这里插入图片描述

  2. 第一步我们还是要使用adil文件,所以还是先开启aidl,操作如上

  3. 我们将服务端的aidl接口,复制到客户端module,包名类名保持一致,直接复制aidl目录就行,如下:
    在这里插入图片描述

  4. 我们创建一个管理类,统一封装接口,供二次开发,注意这里构造方法中,获取服务对象,是和服务端添加服务是相对应的,如下:
    在这里插入图片描述

防止遗漏,还是贴上完整代码:

package com.uniriho.jartest;import static java.lang.Class.forName;import android.annotation.SuppressLint;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;import com.uniriho.androidipc_systemtoolservice.ISystemToolService;import java.lang.reflect.Method;public class SystemToolServiceManager {private String jarvs = "1.0.4";private  final String TAG = "SystemToolServiceManager";public  final String SYSTEM_SERVICE = "systemtool_service";public ISystemToolService mSystemToolService;@SuppressLint("NotConstructor")public SystemToolServiceManager() {try {if (mSystemToolService == null || mSystemToolService.asBinder().isBinderAlive()) {Class serviceManager = forName("android.os.ServiceManager");Method method = serviceManager.getMethod("getService", String.class);IBinder b = (IBinder) method.invoke(serviceManager.newInstance(), SYSTEM_SERVICE);mSystemToolService = ISystemToolService.Stub.asInterface(b);if (mSystemToolService == null) {Log.d(TAG, "get systemtoolservice null!");} else {Log.d(TAG, "get  systemtoolservice service success!");}}} catch (Exception e) {e.printStackTrace();Log.d(TAG, "get systemtoolservice Exception!");}}public String jarVs(){Log.e("SystemToolServiceManager",jarvs);return jarvs;}public void poweroff() {try {mSystemToolService.poweroff();} catch (RemoteException e) {e.printStackTrace();}}public void reboot() {try {mSystemToolService.reboot();} catch (RemoteException e) {e.printStackTrace();}}public boolean startAdbServer() {try {return mSystemToolService.startAdbServer();} catch (RemoteException e) {e.printStackTrace();return false;}}public boolean stopAdbServer() {try {return mSystemToolService.stopAdbServer();} catch (RemoteException e) {e.printStackTrace();return false;}}public boolean setSettingsPassword(String oldPwd, String newPwd) {try {return mSystemToolService.setSettingsPassword(oldPwd, newPwd);} catch (RemoteException e) {e.printStackTrace();return false;}}public boolean resetSettingsPassword() {try {return mSystemToolService.resetSettingsPassword();} catch (RemoteException e) {e.printStackTrace();return false;}}public String getFile(String path) {try {return mSystemToolService.getFile(path);} catch (RemoteException e) {e.printStackTrace();return "";}}public void putFile(String path, String content) {try {mSystemToolService.putFile(path, content);} catch (RemoteException e) {e.printStackTrace();}}public void setProp(String key, String value) {try {mSystemToolService.setProp(key, value);} catch (RemoteException e) {e.printStackTrace();}}public String getBtMac() {try {return mSystemToolService.getBtMac();} catch (RemoteException e) {e.printStackTrace();return "";}}/*** 获取设备编号 16位* @return*/public String getDeviceNumber() {Log.e("getDeviceId",jarvs);return getDeviceSN(0);}/*** 获取设备ID 32位* @return*/public String flashId() {Log.e("getFlashId",jarvs);return getDeviceSN(1);}/*** 获取wlan_mac地址* @return*/public String getDeviceMac(){return getFile("/sys/class/net/wlan0/address");}//获取序列号private String getDeviceSN(int value) {...}public void release(){mSystemToolService = null;}
}
  1. 最后就可以打包了,这里我们既可以选择AndroidStudio编译,也可以build中,添加task makeJar(),根据个人喜好。我这里打包两个,一个jar包,一个aar包:
    在这里插入图片描述

三、验证

1、将服务端打包好的apk安装到设备中,注意,我这里是开机启动,所以需要重启一下,这里我们看到服务已经启动,如下:
在这里插入图片描述

2、创建一个项目,导入第二步的jar包,简单调用其方法
如下:
在这里插入图片描述

验证成功,有疑问或者有错误之处,还请讨论和指导,希望对你开发有帮助。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Kafka【五】Buffer Cache (缓冲区缓存)、Page Cache (页缓存)和零拷贝技术
  • python与pytroch相关
  • linux 下一跳缓存,early demux(‌早期解复用)‌介绍
  • 探索PDF的奥秘:pdfrw库的神奇之旅
  • 32 配置多路由的静态路由
  • https和harbor仓库跟k8s
  • VsCode + Go + macOS 小白 demo运行
  • 浏览器自动化测试的利器:Cypress
  • AI大模型实战:pytorch安装
  • glsl着色器学习(七)
  • 从源码角度分析 Kotlin by lazy 的实现
  • accelerate一些类和函数说明二
  • 集合及映射
  • linux批量解压tar.gz文件
  • 动态规划-最大子数组和
  • php的引用
  • 【407天】跃迁之路——程序员高效学习方法论探索系列(实验阶段164-2018.03.19)...
  • exif信息对照
  • JavaScript/HTML5图表开发工具JavaScript Charts v3.19.6发布【附下载】
  • javascript面向对象之创建对象
  • JS实现简单的MVC模式开发小游戏
  • Magento 1.x 中文订单打印乱码
  • spark本地环境的搭建到运行第一个spark程序
  • windows-nginx-https-本地配置
  • 番外篇1:在Windows环境下安装JDK
  • 关于Java中分层中遇到的一些问题
  • 聊一聊前端的监控
  • 前端性能优化--懒加载和预加载
  • 微信小程序--------语音识别(前端自己也能玩)
  • 我有几个粽子,和一个故事
  • 异常机制详解
  • 在weex里面使用chart图表
  • 【干货分享】dos命令大全
  • 不要一棍子打翻所有黑盒模型,其实可以让它们发挥作用 ...
  • 策略 : 一文教你成为人工智能(AI)领域专家
  • ​【原创】基于SSM的酒店预约管理系统(酒店管理系统毕业设计)
  • ‌移动管家手机智能控制汽车系统
  • # 数论-逆元
  • ###C语言程序设计-----C语言学习(3)#
  • #pragma pack(1)
  • $(this) 和 this 关键字在 jQuery 中有何不同?
  • $.ajax()参数及用法
  • (1)SpringCloud 整合Python
  • (35)远程识别(又称无人机识别)(二)
  • (二)什么是Vite——Vite 和 Webpack 区别(冷启动)
  • (附源码)计算机毕业设计ssm-Java网名推荐系统
  • (几何:六边形面积)编写程序,提示用户输入六边形的边长,然后显示它的面积。
  • (六)Flink 窗口计算
  • (牛客腾讯思维编程题)编码编码分组打印下标(java 版本+ C版本)
  • (强烈推荐)移动端音视频从零到上手(上)
  • (深入.Net平台的软件系统分层开发).第一章.上机练习.20170424
  • (四)事件系统
  • **Java有哪些悲观锁的实现_乐观锁、悲观锁、Redis分布式锁和Zookeeper分布式锁的实现以及流程原理...
  • ..thread“main“ com.fasterxml.jackson.databind.JsonMappingException: Jackson version is too old 2.3.1
  • .CSS-hover 的解释