以系统应用添加服务形式提供给三方app接口调用或者实现业务需求

本文档详细介绍了如何创建一个系统服务模板,以便在Android系统中以内置app的形式添加服务,供三方app调用。内容涉及配置文件的宏控制、AIDL的添加、Selinux权限设置等,旨在解决安全性与耦合性问题。
摘要由CSDN通过智能技术生成


前言

越来越多的客户希望在开发三方app的时候,又要获取一般app无法获取的系统权限。这个时候就需要系统层提供接口给app调用,常规的方式就是将jar包或者aidl文件打包给客户,放在app中使用。但是,如今越来越多的需求修改一些需要系统权限的东西,如MDM之类的,更直接的是在系统进程中直接创建服务,这样做可能有以下问题:
1.安全性:当这个服务在产生crash时,系统进程一旦crash就会表现为手机重启,该现象容易和kernel层引起的KE产生的手机重启混淆(开机动画反复播放),加大排查难度。
2. 耦合性:不好使用宏控,代码层面还好用宏控制,不过一些附带的资源文件,比如drawable、layout等不好控制,即使同平台其它项目没有用到该功能,不过资源文件依然会占用掉系统的资源。但是倘若把服务逻辑和资源都放在app里面,岂不是就可以通过宏控决定?
以下是总结了创建以内置app形式添加的系统服务模板。

一、概述

以下提供的模板中:
XXX,Xxx,xxx都为自定义,自己可以随意定义,只要大小写的地方要统一
var function():函数名,请根据实际需要修改

二、在配置文件中定义宏控

目的是增加一个开关,不同项目决定是否编译该APP

1.开宏,device/项目目录/ProjectConfig.mk //路径根据平台而定

 TEST_XXX_MANAGER = yes
  1. 宏控逻辑,控制app和服务模块, common.mk
ifeq ($(strip TEST_XXX_MANAGER yes)
       PRODUCT_PACKAGES += Xxx
       PRODUCT_PROPERTY_OVERRIDES += ro.test.xxx_manager = true //属性根据你平台来定,注意有些平台可能定义这个属性后开机获取不到
endif

三、在framework添加aidl

1.将aidl加入编译: frameworks/base/Android.mk//根据平台、Android版本不同也有可能是Android.bp,也可能不用配置,请灵活应对

...
      core/java/com/test/xxx/IXxx.aidl \
...

2.注册服务, frameworks/base/core/java/android/app/SystemServiceRegistry.java

...
import com.test.xxx.XxxFeatureOption;
import com.test.xxx.XxxManager;
import com.test.xxx.XxxUtils;
...
    static {
...
 if (XxxFeatureOption.TEST_XXX_MANAGER) {
 	registerService(XxxUtils.SERVICE, XxxManager.class, new CachedServiceFetcher<XxxManager>() {
 	@Override
 	public XxxManager createService(ContextImpl ctx) {
 		return new XxxManager(ctx);
 	}
 		});
 }
...

3.添加aidl文件, frameworks/base/core/java/com/test/xxx/IXxx.aidl

package com.test.xxx;
/**
 * @hide
 */
interface IXxx {
 var function();
}

4.此文件用来读取是否加服务,通过TEST_XXX_MANAGER判断, frameworks/base/core/java/com/test/xxx/XxxFeatureOption.java

package com.test.xxx;

import android.os.SystemProperties;

public class XxxFeatureOption {
public static final boolean TEST_XXX_MANAGER = SystemProperties.getBoolean(“ro.test.xxx_manager”, false);
}

5.添加aidl对应的manager,其中manager可以获取对应的服务 frameworks/base/core/java/com/test/xxx/XxxManager.java

package com.test.xxx;

import android.app.ActivityThread;
import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
import android.util.Singleton;

public final class XxxManager {
 private static final String TAG = "XxxManager";
 private static boolean sIsInitialized = false;

 /**
  * @hide
  */
 public XxxManager(Context context) {
 	context = context.getApplicationContext();
 if (context == null) {
 throw new IllegalArgumentException("context not associated with any application (using a mock context?)");
 }
 try {
 if (!sIsInitialized) {
 			getService();
 			sIsInitialized = true;
 			}
 	} catch (UnsupportedOperationException e) {
 		Log.e(TAG, "WL_DEBUG XxxManager e = " + e, e);
 }
 }

 public static final XxxManager getInstance() {
 		Context context = ActivityThread.currentApplication();
 if (context == null) {
 			throw new IllegalArgumentException("context cannot be null");
 		}
 		context = context.getApplicationContext();
 if (context == null) {
 			throw new IllegalArgumentException("context not associated with any application (using a mock context?)");
 }
 /* use getSystemService() for consistency */
 		XxxManager manager = (XxxManager) context.getSystemService(XxxUtils.SERVICE);
 		return manager;
 }

 private static IXxx getService() {
 		return IXxxSingleton.get();
 }

 	private static final Singleton<IXxx> IXxxSingleton = new Singleton<IXxx>() {
 		@Override
 	protected IXxx create() {
 		IBinder b = ServiceManager.getService(XxxUtils.SERVICE);
 if (b == null) {
 			Log.e(TAG, "WL_DEBUG create failed", new Throwable());
 			return null;
 }
 return IXxx.Stub.asInterface(b);
 		}
 	};

 	public var function() {
 var result = null;
 		try {
 			result = getService.function();
 		} catch (RemoteException e) {
 			throw e.rethrowFromSystemServer();
 }
 	return result;
 }
}

6.此文件用来保存服务名称和app包名等字符串,方便调用: frameworks/base/core/java/com/test/xxx/XxxUtils.java

package com.test.xxx;

import java.util.List;

import android.app.ActivityThread;
import android.app.Application;

public class XxxUtils {
 	public static final String SERVICE = "test_xxx";
 	public static final String PACKAGE = "com.test.xxx";
}

7.将com/test/xxx加到package_allowed_list.txt中
需要将在frameworks下新增的路径加到package_allowed_list.txt,否则会编译报错。
build/soong/scripts/check_boot_jars/package_allowed_list.txt

+com\.test\.xxx

四、app模板

app目录/Xxx/Android.mk:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)
LOCAL_CERTIFICATE := platform
LOCAL_MODULE_TAGS := optional
LOCAL_PACKAGE_NAME := Xxx
LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_PRIVILEGED_MODULE := true
LOCAL_SRC_FILES := $(call all-java-files-under, src)
include $(BUILD_PACKAGE)

include $(call all-makefiles-under,$(LOCAL_PATH))
app目录/Xxx/AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.test.xxx"
    android:sharedUserId="android.uid.system">                                    -------------- 有了这句话,你就有了系统权限,绝大部分permission都不用声明

    <application
        android:name=".XxxApplication"
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:persistent="true">                                                -------------- 有了这句话,你的进程就永远存在,开机就唤醒,比谁都早,谁都杀不死你
<!--这里写你自己的代码-->
    </application>

</manifest>
app目录/Xxx/res/values/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">Xxx</string>
<!--这里写你自己的代码-->
</resources>
app目录/Xxx/src/com/test/xxx/XxxApplication.java	
package com.test.xxx;

import java.util.Iterator;
import java.util.List;
import android.app.ActivityManager;
import android.app.Application;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.os.Process;

public class XxxApplication extends Application {
 private static final String TAG = "XxxApplication";
 private XxxService mXxxService;

// private XxxYyyManager mXxxYyyManager;//按需要创建你自己的M
 @Override
 public void onCreate() {
 super.onCreate();
 boolean isMainProcess = false;
 ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
 List processes = am.getRunningAppProcesses();
 Iterator i = processes.iterator();
 while (i.hasNext()) {
 RunningAppProcessInfo appInfo = (RunningAppProcessInfo) (i.next());
 if (appInfo.pid == Process.myPid()) {
 isMainProcess = (XxxUtils.PACKAGE.equals(appInfo.processName));
 break;
 }
 }
 if (UserHandle.myUserId() == 0 && isMainProcess) {
 mXxxService = new XxxService(this);
// mXxxYyyManager = new XxxYyyManager(this);
//这里写你自己的代码,用于初始化代码,建议基于MVC的模式编辑,XxxApplication充当C(Control)的角色,相同模块的代码大于一行建议封装成一个M(Model),命名可以XxxYyyManager的形式命名,构造函数以public XxxYyyManager(XxxApplication app)与C交互,各M之间不建议直接交互
 }
 }

 public var function() {
 var result = ...;
 ...
 return result;
 }
}
app目录/Xxx/src/com/test/xxx/XxxService.java
package com.test.xxx;

import android.os.ServiceManager;

public class XxxService extends IXxx.Stub {
 private final XxxApplication mApp;

 public XxxService(XxxApplication xxxApplication) {
 mApp = xxxApplication;
 ServiceManager.addService(XxxUtils.SERVICE, this);
 }

 @Override
 public var function() {
 return mApp.function();
 }
}

app目录/Xxx/src_for_user/com/test/xxx/XxxManager.java//可选,如果你的功能需要提供给客户使用的,就提供这个文件就行,告诉客户通过XxxManager.getInstance获取对象,然后通过获取到的对象执行各个函数即可,该文件不参与系统编译,所有接口全部留空,无需实现

package com.test.xxx;

public final class XxxManager {
 public static final XxxManager getInstance() {
 return null;
 }

 public var function() {
 return null;
 }
}

五、为服务和app添加selinux权限

适用于:Android P及以上版本,sepolicy相关文件保证最后一个字符是回车换行,不然会出现异常

1.新建一个te文件来给新增加的app构建一个运行所在的domain,如test_xxx_app.te
device/…/sepolicy/…/test_xxx_app.te//各平台位置有细微差异,请灵活应对

type test_xxx_app, domain;   //为app添加domain
typeattribute test_xxx_app coredomain, mlstrustedsubject;  //给定核心域,后面的mlstrustedsubject可选,用于需要给客户提供app目录/Xxx/src_for_user/com/test/xxx/XxxManager.java文件的项目

app_domain(test_xxx_app)  //注意不用分号,给app应用域权限
net_domain(test_xxx_app)   //注意不用分号,给app网络域权限

add_service(test_xxx_app, test_xxx_service)  //把新增的服务关联到app,相当于add
allow test_xxx_app activity_service:service_manager find; //允许app访问ActivityService服务,不然会报avc错

2.给priv_app目录下的app访问新增服务的权限,因为我们的app就是放在这个目录 device/…/sepolicy/…/priv_app.te

...
allow priv_app test_xxx_service:service_manager find;
...

3.给新增的服务添加 系统服务进程 的权限, device/…/sepolicy/…/system_server.te

...
allow system_server test_xxx_service:service_manager find;
...

4.如果是暴露接口给三方用,则需要加这个权限 device/…/sepolicy/…/untrusted_app_all.te//可选,用于需要给客户提供app目录/Xxx/src_for_user/com/test/xxx/XxxManager.java文件的项目

...
allow untrusted_app_all test_xxx_service:service_manager find;
...
  1. 给新增的服务定义域,给上下文, device/…/sepolicy/…/service_contexts
...
test_xxx                            u:object_r:test_xxx_service:s0
...

6.给新增的服务定义类型,类型为 service_manager_type, device/…/sepolicy/…/service.te

...
type test_xxx_service, service_manager_type, mlstrustedobject;
...

7.将新增的app和对应的domain关联起来, device/…/sepolicy/…/seapp_contexts

...
user=system seinfo=platform name=com.test.xxx domain=test_xxx_app type=app_data_file levelFrom=user
...

五、添加接口白名单(可选)

给第三方调用的话必须添加,否则可以不用。
frameworks/base/config/hiddenapi-greylist.txt//可选,用于需要给客户提供app目录/Xxx/src_for_user/com/test/xxx/XxxManager.java文件的项目

...
Lcom/test/xxx/XxxManager;->getInstance()Lcom/test/xxx/XxxManager;
Lcom/test/xxx/XxxManager;->function()Lvar;        ----------------请根据实际情况修改

按jni接口格式写,若不知道怎么写可以先编译,然后在生成的out\soong\hiddenapi中找到相应接口复制过来即可

Android P及以上版本:
frameworks/base/config/hiddenapi-greylist.txt//可选,用于需要给客户提供app目录/Xxx/src_for_user/com/test/xxx/XxxManager.java文件的项目
Android S及以上版本:
frameworks/base/boot/hiddenapi/hiddenapi-unsupported.txt
若没有添加白名单,三方应用在通过XxxManager调用接口时,会出现如下报错:
在这里插入图片描述

PS:调试手段

adb shell下,敲ps -A | grep “关键词”,查查加的app进程运行了没有,以下结果是app在运行中:
K2-35:/ # ps -A | grep “test”
system 2493 590 14737064 87036 do_epoll_wait 0 S com.test.xxx

adb shell下,敲service list | grep “关键词”,查查app中服务add进去了没有,以下是在机器上adb下的显示结果:
K2-35:/ # service list | grep “test”
7 test_xxx: [com.test.xxx.IXxx]
其中“test_xxx”正是你在frameworks/base/core/java/android/app/SystemServiceRegistry.java注册服务的名字


总结

以上总结了如何添加一个完整的服务的模板,值示例了一个函数:var function():函数名
若要增加接口,则仿照该接口添加即可,需要同时修改以下文件:
frameworks/base/core/java/com/test/xxx/XxxManager.java
frameworks/base/core/java/com/test/xxx/IXxx.aidl
app目录/Xxx/src/com/test/xxx/XxxService.java
app目录/Xxx/src/com/test/xxx/XxxApplication.java
app目录/Xxx/src_for_user/com/test/xxx/XxxManager.java//可选,如果你的功能需要提供给客户使用的,就提供这个文件就行,告诉客户通过XxxManager.getInstance获取对象,然后通过获取到的对象执行各个函数即可,该文件不参与系统编译,所有接口全部留空,无需实现

最后,若是提供接口给客户的三方普通app使用:
1、提供
app目录/Xxx/src_for_user/com/test/xxx/XxxManager.java该文件给客户,XxxManager.getInstance获取对象,然后通过获取到的对象执行各个函数即可,
注意XxxManager位于应用中的位置包路径要为com.test.xxx.XxxManager,与framework下添加的XxxManager路径一致
2、使用方法:

import com.test.xxx.XxxManager;
    XxxManager mXxxManager = XxxManager.getInstance(); 
    mXxxManager.function();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值