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

news/2024/9/17 4:17:40/ 标签: android, jar, java

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

一、编写服务端

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

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

java">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();}}

完整代码如下:

java">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;}
}
  • 先配置接收开机广播、然后启动服务,这里也可以在开机广播启动应用,将应用设置为无界面应用,这里我们启动一个前台服务。关于未启动应用无法接受开机广播可以参考这部分内容。
java">//添加权限:<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类,然后实现其方法,。如下:
    在这里插入图片描述
    这样,我们的服务端就完成了,完整代码如下:
java">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. 我们创建一个管理类,统一封装接口,供二次开发,注意这里构造方法中,获取服务对象,是和服务端添加服务是相对应的,如下:
    在这里插入图片描述

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

java">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包,简单调用其方法
如下:
在这里插入图片描述

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


http://www.ppmy.cn/news/1521826.html

相关文章

Linux 常用命令 - hexdump 【以指定格式显示文件内容】

简介 hexdump 可以将指定文件或标准输入按照指定的格式进行输出&#xff0c;其可以用来查看任何文件的原始数据&#xff0c;在分析非文本文件的场景下非常有用。 使用方式 hexdump [-bcCdovx] [-e 指定格式] [-f 指定文件] [-n 长度] [-s 偏移] file ... hd [-bcdovx] [-e 指…

Leetcode每日刷题之155.最小栈

1.题目解析 本题是实现一个栈并且要实现其中的插入、删除、返回栈顶元素、返回最小元素的函数&#xff0c;这里主要的难点就是返回最小元素的函数&#xff0c;如果我们直接遍历&#xff0c;那么时间复杂度就是O(N)&#xff0c;但是题目要求我们需要在常数时间也就是O(1)的时间复…

Docker必备命令集合,让你轻松驾驭容器化

Docker作为现代化应用程序的部署和管理平台&#xff0c;已经成为开发者和运维工程师的得力工具。但对于新手而言&#xff0c;面对众多的命令和参数&#xff0c;有时会感到困惑。本文将为你总结一组常用的Docker命令&#xff0c;助你快速上手并高效使用这一强大工具。 1. 基础命…

Qt数字化信息通讯调制解调

对于数字化信息通讯调制解调&#xff0c;Qt本身并不直接提供调制解调的功能&#xff0c;但是可以通过Qt的网络编程接口&#xff0c;结合相关的算法和硬件设备来实现。例如&#xff0c;可以通过Qt的信号处理库来实现数字信号的调制和解调算法&#xff0c;或者通过串口通信与外部…

word中怎么快速选中光标之前或之后的全部内容?

在Word中&#xff0c;快速选中光标之后的全部内容的快捷键&#xff1a;Ctrl Shift End&#xff1b; 在Word中&#xff0c;快速选中光标之前的全部内容的快捷键&#xff1a;Ctrl Shift Home。 在Word中&#xff0c;选取的快捷键如下。 一、选定整个文本&#xff1a; 1&#…

微信小程序认证和备案

小程序备案的流程一般包括以下步骤‌&#xff1a; 准备备案所需材料‌&#xff1a;通常需要提供‌营业执照、法人的‌身份证、两个‌手机号和一个邮箱等资料。 ‌1 ‌登录‌微信公众平台‌&#xff1a;作为第一次开发微信小程序的服务商&#xff0c;需要通过微信公众平台申请…

掌握Git分支管理策略:让团队协作更高效

在现代软件开发过程中&#xff0c;版本控制系统&#xff08;VCS&#xff09;是不可或缺的一部分。Git作为目前最流行的分布式版本控制系统之一&#xff0c;为开发者提供了强大的工具集来管理代码变更历史。然而&#xff0c;仅仅掌握Git的基本命令并不足以应对大型项目和团队协作…

中间代码例题

答案&#xff1a;D 知识点&#xff1a; 中间代码是一种简单且含义明确的记号系统&#xff0c;可以有若干形式&#xff0c;它们的共同特征是与机器无关。 最常见的中间代码有&#xff1a;后缀式&#xff0c;语法树&#xff0c;三地址码&#xff0c;四元式 这些往往是数据&am…

HarmonyOS开发实战( Beta5版)Stack组件实现滚动吸顶效果实现案例

介绍 本示例介绍运用Stack组件以构建多层次堆叠的视觉效果。通过绑定Scroll组件的onScroll滚动事件回调函数&#xff0c;精准捕获滚动动作的发生。当滚动时&#xff0c;实时地调节组件的透明度、高度等属性&#xff0c;从而成功实现了嵌套滚动效果、透明度动态变化以及平滑的组…

linux中普通用户免密切换root

在 Linux 中如果要实现不输入密码直接切换到 root 权限&#xff0c;可以通过配置 sudoers 文件来实现。但这种方式有一定安全风险&#xff0c;使用时需谨慎。 以下是具体步骤&#xff1a; 1.以当前有 sudo 权限的用户身份打开终端。 2.使用以下命令编辑 /etc/sudoers 文件&…

WordPress安装指南:主题、插件和最佳实践

WordPress是世界上最流行的内容管理系统&#xff08;CMS&#xff09;&#xff0c;因其易用性和灵活性而备受欢迎。本文将指导您完成WordPress的安装过程&#xff0c;介绍一些常用的主题和插件&#xff0c;并分享一些重要的注意事项。 1. WordPress安装 步骤1&#xff1a;准备…

数字时代,寻找新的生意增长点之前要做什么准备?

要做好最基础也最繁复的数据管理。 在竞争日益激烈的快消市场中&#xff0c;企业面临前所未有的挑战与压力。在这种高压环境下&#xff0c;数字化转型不再仅仅是选择&#xff0c;而是企业探索新的业务增长点、保持竞争优势的关键战略。然而&#xff0c;随着企业数字化进程的加…

MATLAB进行天线阵列方向图综合

摘要&#xff1a;本次推文将介绍如何利用MATLAB的Sensor Array Analyzer进行天线阵列的方向图综合。 1. 阵列方向图综合理论 对于均匀平面阵列而言&#xff0c;其阵因子公式可以写成 当阵列是三角网格布置或者圆环阵时&#xff0c;《ANTENNA THEORY ANALYSIS AND DESIGN》等相…

Postgresql表和索引占用空间回收释放(表空间膨胀)

Postgresql表和索引占用空间回收释放&#xff08;表空间膨胀&#xff09; -- 1.创建测试表t_user create table if not exists t_user(id serial primary key,user_name varchar(255),pass_word varchar(255),create_time date,dr char(1) );create index ind_time on t_user(c…

【学习笔记】SSL证书安全机制之证书验证

前言&#xff1a;每当Client从Server收到一张证书&#xff0c;有2件事Client需要去验证&#xff1a; 证书是否有效&#xff1f; 证书只是文件中的文本Client如何知道内容能够信任&#xff1f;Server是否是证书真正的拥有者&#xff1f; 证书可以公开获取Client如何知道Server是…

rsync搭建全网备份

rsync搭建全网备份 1. 总体概述1.1 目标1.2 简易指导图1.3 涉及工具或命令1.4 环境 2. 实施2.1 配置备份服务器2.2 备份文件准备2.3 整合命令2.4 扩展功能 1. 总体概述 1.1 目标 本次搭建目标&#xff1a; 每天定时把服务器数据备份到备份服务器备份完成后进行校验把过期数据…

代码随想录 -- 二叉树 -- 二叉树的最小深度

111. 二叉树的最小深度 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a;递归调用 递归返回值&#xff1a;返回以当前节点为根节点的二叉树的最小深度 递归出口&#xff1a;当根节点为空时&#xff0c;返回 0 单层递归逻辑&#xff1a;特殊情况处理&#xff1a;当…

【区块链 + 人才服务】区块链教学管理平台 | FISCO BCOS应用案例

面对传统教育行业存在的教育过程难监督、教育信息不公开、教育效果难认定、教务管理缺抓手、数据造假和证 书局限性等诸多痛点问题。北京奕江科技有限公司基于 FISCO BCOS 底层开发区块链教学管理平台&#xff0c;搭建区块链 教学管理系统、区块链课程学习及实训环境&#xff0…

HTTP中常用的4种请求方式——前端如何发送?后端怎么接受?

一.Get请求&#xff1a; 1.什么是Get请求&#xff1f; 2.前后端如何使用Get交互&#xff1f; 2.1.Query参数格式的Get请求 2.2.Path参数格式的Get请求 二.Post请求&#xff1a; 1.什么是Post请求&#xff1f; 2.前后端如何使用Post交互&#xff1f; 三.Put请求&#xf…

C语言代码练习(第十五天)

今日练习&#xff1a; 37、输入连个正整数 n 和 m &#xff0c;求其最大公约数和最小公倍数 38、请编程序将“China”翻译成密码&#xff0c;密码规律是&#xff1a;用原来的字母后面第4个字符代替原来的字母 39、设半径 r 1.5&#xff0c;圆柱高 h 3&#xff0c;求圆周长、圆…