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

news2024/12/23 20:57:43

概述:
如果你是做技术支持,有没有遇到这种情况,客户既要实现具备系统权限的功能,但是呢,又不想把自己的应用做成系统应用。这时候你咋办。
我们可以添加一个具备系统权限的服务,不管前台的,还是后台的,都可以。今天的栗子,我们就用前台服务,为啥用前台服务了,因为状态栏我可以很清楚的看台它在运行。实现原理,还是通过进程通信,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);

        //创建NotificationChannel

        if (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;

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "SystemToolService onStartCommand");
        Log.d(TAG, "AIDLService isServiceAlive==" + isServiceAlive(this));
        publish();
        return START_STICKY;
    }

    @Override
    public 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
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

  • 先配置接收开机广播、然后启动服务,这里也可以在开机广播启动应用,将应用设置为无界面应用,这里我们启动一个前台服务。关于未启动应用无法接受开机广播可以参考这部分内容。
//添加权限:
 <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
//注册广播
  <receiver
            android: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)
    @Override
    public 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();
        }
    }

    @Override
    public 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);
        }
    }

    @Override
    public void reboot() throws RemoteException {
        Log.e(TAG,"reboot");
        powerManager.reboot("");
    }

    @Override
    public boolean startAdbServer() throws RemoteException {
       ,,,
    }

    @Override
    public boolean stopAdbServer() throws RemoteException {
       ,,,
    }

    @Override
    public boolean setSettingsPassword(String oldPwd, String newPwd) throws RemoteException {
       ,,,
    }

    @Override
    public boolean resetSettingsPassword() throws RemoteException {
      ,,,
    }

    @Override
    public String getFile(String path) throws RemoteException {
       ,,,
    }

    @Override
    public void putFile(String path, String content) throws RemoteException {
       ,,,
    }

    @Override
    public void setProp(String key, String value) throws RemoteException {
      ,,,
    }

    @Override
    public 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.androidipc_systemsootlservice.sdk.systemtool;

import static java.lang.Class.*;

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";
    private  SystemToolServiceManager instance;

    public  ISystemToolService mSystemToolService;
    public  final String SYSTEM_SERVICE = "systemtool_service";

    @SuppressLint("NotConstructor")
    public SystemToolServiceManager SystemToolServiceManager() {
        if (instance == null) {
            instance = new 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!");
        }
        return instance;
    }


    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(){
        instance = null;
        mSystemToolService = null;
    }
}

  1. 最后就可以打包了,这里我们既可以选择AndroidStudio编译,也可以build中,添加task makeJar(),根据个人喜好。我这里打包两个,一个jar包,一个aar包:
    在这里插入图片描述

三、验证

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2096969.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

原来这就是 布隆过滤器

1.布隆过滤器的引出 一个有趣的现象 不知道大家有没有发现这么一个现象&#xff0c;当我们在使用一些软件的时候&#xff0c;比如像 CSDN、这种具有推荐算法的应用&#xff0c;他并不会给我们推送我们已经浏览过的内容&#xff0c;这是怎么做到的呢&#xff1f; 说白了就是人…

【物理教学】高中物理速度时间练习

速度时间图像代码 这段代码是一个使用Python编写的脚本&#xff0c;它利用matplotlib库来绘制物理问题中的速度-时间图。代码的主要优点如下&#xff1a; 用户交互&#xff1a;代码通过input函数与用户进行交互&#xff0c;允许用户输入物理问题的参数&#xff0c;如初始速度…

钢结构厂房通风天窗使用场景探讨

钢结构厂房通风天窗作为现代建筑中高效通风的解决方案&#xff0c;广泛应用于多个领域&#xff0c;为各类建筑提供优质的室内环境。成都昱合昇带大家一起探讨通风天窗在不同使用场景下的表现。 1、工业厂房降温 工业厂房是通风天窗典型的应用场景之一。在高温季节或生产过程中产…

苏州科技大学商学院:加强生态保护,推动绿色发展

原标题&#xff1a;苏州科技大学商学院&#xff1a;加强生态保护&#xff0c;推动绿色发展&#xff0c;在美丽中国建设中贡献青春力量 建设美丽中国是全面建设社会主义现代化国家的重要目标&#xff0c;也是激励全国人民为实现中华民族伟大复兴中国梦而共同奋斗的伟大旗帜。中…

CSS3 文本效果(text-shadow,box-shadow,white-space等)文本溢出隐藏并且显示省略号

一 text-shadow text-shadow 属性是 CSS3 中用于为文本添加阴影效果的工具。它可以增强文本的可读性和视觉吸引力&#xff0c;提供丰富的视觉效果 1 语法 text-shadow: offset-x offset-y blur-radius color;offset-x&#xff1a;阴影相对于文本的水平偏移量。可以是正值&am…

STM32CUBEIDE FreeRTOS操作教程(四):timer软件定时器

STM32CUBEIDE FreeRTOS操作教程&#xff08;四&#xff09;&#xff1a;timer软件定时器 STM32CUBE开发环境集成了STM32 HAL库进行FreeRTOS配置和开发的组件&#xff0c;不需要用户自己进行FreeRTOS的移植。这里介绍最简化的用户操作类应用教程。以STM32F401RCT6开发板为例&am…

18047 水仙花数

### 思路 1. 遍历所有的三位数&#xff08;100到999&#xff09;。 2. 对于每个数&#xff0c;提取其百位、十位和个位数字。 3. 计算这些数字的立方和。 4. 如果立方和等于原数&#xff0c;则该数是水仙花数&#xff0c;输出该数。 ### 伪代码 1. 遍历i从100到999&#xff1a…

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…

数据库操作与集成:使用Python与SQLite、MySQL、PostgreSQL等数据库

目录 引言 一、Python与SQLite的集成 1.1 SQLite简介 1.2 连接SQLite数据库 1.3 创建表 1.4 插入数据 1.5 查询数据 1.6 更新和删除数据 二、Python与MySQL的集成 2.1 MySQL简介 2.2 安装与配置 2.3 连接MySQL数据库 2.4 创建表与插入数据 2.5 查询、更新与删除数…

笔记:《利用Python进行数据分析》之apply的应用

这一节较难&#xff0c;十分灵活&#xff0c;可多花点时间 apply的简单应用 最通用的GroupBy方法是apply。 apply会将待处理的对象拆分成多个片段&#xff0c;然后对各片段调用传入的函数&#xff0c;最后尝试将各片段组合到一起。 回到之前那个小费数据集&#xff0c;假设你…

(四)vForm 动态表单自定义组件、属性

系列文章目录 (一)vForm 动态表单设计器之使用 (二)vForm 动态表单设计器之下拉、选择 (一)vForm 动态表单设计器之使用 文章目录 前言 一、自定义字段组件 1. 获得自定义组件json 2. 源码修改 二、自定义属性面板 1.属性面板文件 2.添加自定义属性 3.为字段组件添加属…

同事用10分钟给公司做了一套数据大屏,实力选手非他莫属!

数据可视化大屏是什么&#xff1f; 数据可视化大屏是一种将大量数据以图形、图表、地图等直观形式展示在大屏幕上。它通常被应用于企业的监控中心、会议室、展厅等场所&#xff0c;用于实时展示企业的关键业务指标、运营数据、市场趋势等信息。 今天给大家分享用JVS-智能BI如何…

DBdoctor快速纳管GBase 8a数据库

目录 如何快速纳管GBase 8a&#xff1f; 1.GBase 8a分析型数据库纳管部署架构 2.一分钟零依赖DBdoctor Server安装 3.快速纳管GBase 8a 重点说明&#xff1a; 针对GBase 8a&#xff0c;DBdoctor提供哪些功能服务&#xff1f; 1.SQL审核 2.深度巡检与报表 3.性能洞察 1&…

WebSocket通信学习笔记

1 简介 WebSocket是一种全双工通信协议&#xff0c;它允许客户端和服务器之间建立持久化的双向连接&#xff0c;从而在不频繁创建HTTP请求的情况下进行实时数据传输。与传统的HTTP协议相比&#xff0c;WebSocket更适合需要实时数据更新的应用场景&#xff0c;如聊天应用、实时…

架构师篇-23、工作坊实战应用架构

复习 ADM - 应用架构【AA】 案例实践 - 应用组件 - 核心模块 案例实践 - xx 项目应用关系 课程内应用架构

科研绘图系列:R语言PCoA图(PCoA plot)

介绍 PCoA(主坐标分析,Principal Coordinate Analysis)是一种多维数据的降维技术,它用于探索高维空间中样本之间的关系。PCoA通常用于生态学、遗传学和其他领域的数据分析,以揭示样本或个体之间的相似性或差异性。 PCoA图的作用: 数据降维:PCoA可以将高维数据(如物种…

18046 字母分类统计

### 思路 1. 读取输入的一行字符。 2. 初始化计数器&#xff1a;字母、数字、空格和其它字符的个数。 3. 遍历每个字符&#xff0c;根据其类型更新相应的计数器。 4. 输出计数结果&#xff0c;格式为&#xff1a;字母、数字、空格和其它字符的个数&#xff0c;中间以空格分隔。…

【2024-2025源码+文档+调试讲解】公开课管理系统

摘 要 随着互联网技术的迅猛发展&#xff0c;教育行业也逐渐迎来了一场全新的变革。在线教育平台的崛起为学习者提供了更加便捷灵活的学习方式&#xff0c;而公开课作为其中的一种形式&#xff0c;因其开放性和多样性而备受欢迎。然而&#xff0c;传统的公开课管理方式存在着…

【案例】如何做B端竞品分析?

竞品分析是产品经理的基本功&#xff0c;B端产品经理同样也需要经常做竞品分析。 B端产品的竞品分析难度更大&#xff0c;主要体现在如下几个方面&#xff1a; 1&#xff09;B端产品的信息获取困难 产品试用成本高&#xff0c;不像互联网产品那样可以随时下载体验。 对外公…

【数学分析笔记】第3章第1节 函数极限(1)

3. 函数极限与连续函数 3.1 函数极限 设有一半径为 r r r的圆&#xff0c;角度 x x x用弧度制表示。 红色的弧长为 2 x r 2xr 2xr&#xff0c;蓝色的弦长为 2 r sin ⁡ x 2r\sin x 2rsinx y 弦长 弧长 sin ⁡ x x y\frac{弦长}{弧长}\frac{\sin x}{x} y弧长弦长​xsinx​…