概述:
如果你是做技术支持,有没有遇到这种情况,客户既要实现具备系统权限的功能,但是呢,又不想把自己的应用做成系统应用。这时候你咋办。
我们可以添加一个具备系统权限的服务,不管前台的,还是后台的,都可以。今天的栗子,我们就用前台服务,为啥用前台服务了,因为状态栏我可以很清楚的看台它在运行。实现原理,还是通过进程通信,AIDL实现。我们可以将服务端内置到aosp中,或者直接将服务端源码内置aosp中,下面我们就直接打包成apk,然后内置,客户端,我们可以直接打包成jar包,封装需要系统权限的接口,供二次开发。
先介绍一下,我们整个项目的目录。app模块是我们内置的服务端,SystemToolService是打包的客户端,JarTest就是我们最终测试jar包的简单demo。
一、编写服务端
-
我们先创建个项目,配置为系统权限android:sharedUserId=“android.uid.system”,并添加系统签名。
-
创建服务,并添加一个自定义服务,其他部分和前面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) {
...
}
}
二、客户端部分,也就是打包部分
-
创建一个Module,选择Android library
-
第一步我们还是要使用adil文件,所以还是先开启aidl,操作如上
-
我们将服务端的aidl接口,复制到客户端module,包名类名保持一致,直接复制aidl目录就行,如下:
-
我们创建一个管理类,统一封装接口,供二次开发,注意这里构造方法中,获取服务对象,是和服务端添加服务是相对应的,如下:
防止遗漏,还是贴上完整代码:
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;
}
}
- 最后就可以打包了,这里我们既可以选择AndroidStudio编译,也可以build中,添加task makeJar(),根据个人喜好。我这里打包两个,一个jar包,一个aar包:
三、验证
1、将服务端打包好的apk安装到设备中,注意,我这里是开机启动,所以需要重启一下,这里我们看到服务已经启动,如下:
2、创建一个项目,导入第二步的jar包,简单调用其方法
如下:
验证成功,有疑问或者有错误之处,还请讨论和指导,希望对你开发有帮助。