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);

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

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

  • 27
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

这个Bug有点难搞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值