前言
本文基于android 8.0 (Oreo) 系统,添加一个开机自启动的系统级服务,需要先搭建好android源码编译环境
服务的添加分为以下步骤进行
- 创建服务
- 将服务注册到开机启动
- 给服务添加
selinux
权限 - 编译运行
以下博文中很多系统脚本内容较多,用
...
省略了
一、创建服务
为了方便开发 我们首先用
Android Studio
进行基础的代码编写
- 在
main/aidl
下创建ITestService.aidl
// ITestService.aidl
package com.my.service;
// Declare any non-default types here with import statements
interface ITestService {
String sayHello();
}
- 在
main/java
下创建TestServiceManager
package com.my.service; //包名与aidl文件一样
import android.content.Context;
import android.os.RemoteException;
public class TestServiceManager {
//context变量可写可不写
private Context mContext;
//manager 是TestService的代理类,持有TestService的一个实例
private ITestService mService;
public TestServiceManager(Context context, ITestService service) {
this.mContext = context;
this.mService = service;
}
public String sayHello() {
try {
return mService.sayHello();
} catch (RemoteException e) {
e.printStackTrace();
}
return null;
}
}
- 创建TestService.java
//这里一定要注意包名 因为这个类需要放到源码目录
///frameworks/base/services/core/java/com/android/server 包下 其包名为com.android.server,
//所以我们在Android Studio里面也创建同样的包名,这样后续就不用修改
package com.android.server;
import android.os.RemoteException;
import com.my.service.ITestService;
public class TestService extends ITestService.Stub {
private Context mContext;
public TestService(Context context) {
this.mContext = context;
Log.d(TAG, "TestService已经启动");
}
@Override
public String sayHello() throws RemoteException {
return "this is a local service";
}
}
二、将服务添加到系统源码中
- aidl与manager的添加
service与aidl的添加路径可以是
frameworks/base/core/java/android/app
,系统的大部分aidl
和manager
也在这个目录下,但是这里我们为了与系统的服务分开,选择新建一个模块来存放(这么做的目的也是为了方便后续导出jar包供客户端调用)
- 在
frameworks/base
下新建一个文件夹mytest
- 新建
mytest/java/com/my/service
文件夹,这里com/my/service
对应的是上面创建的aidl
的包名,然后将ITestService.aidl
和TestServiceManager.java
放到该目录; - 新建
mytest/Android.mk
文件 内容如下,我们的模块名称就是mytest
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, java)
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_MODULE := mytest
include $(BUILD_JAVA_LIBRARY)
- 由于我们新增了一个模块
mytest
(在系统中,每个包含Android.mk的目录视为一个模块),需要再/build/core/pathmap.mk
中添加
FRAMEWORKS_BASE_SUBDIRS := \
$(addsuffix /java, \
core \
graphics \
location \
media \
media/mca/effect \
media/mca/filterfw \
media/mca/filterpacks \
drm \
opengl \
sax \
telecomm \
telephony \
wifi \
keystore \
rs \
#新增的mytest
mytest \
)
- 修改
framwork/base/Android.mk
LOCAL_SRC_FILES += ... \
#添加这一句
mytest/java/com/my/service/ITestService.aidl \
#
packages_to_document := ... \
#添加这一句
com/my/service
修改build/target/product/base.mk
PRODUCT_PACKAGES += \
... \
#加上我们自己的模块名称(必须和Android.mk中的模块名称一致)
mytest
- 修改
build/target/product/generic_no_telephony.mk
PRODUCT_PACKAGES += \
... \
mytest
- 修改
build/core/tasks/check_boot_jars/package_whitelist.txt
如果不修改这个文件,编译会报以下错误
xxxxxx/classes.jar: unknown package name of class file xxxxxx
...
...
# 新增我们自定义的包名
com\.my\.service
通过以上配置,模块的添加就完成了
三、将service添加到系统中
将我们创建的
TestService.java
复制到系统目录frameworks/base/services/core/java/com/android/server/
下(可以看到改目录下有很多系统的Servcie
)在
frameworks/base/core/java/android/content/Context.java
中添加常量
public static final String LU_SERVICE = "lu_service";
- 添加服务到
frameworks/base/services/java/com/android/server/SystemServer.java
//记得导包
import com.android.server.TestService
private void startOtherServices() {
...
...
//添加
traceBeginAndSlog("addLuTestService");
ServiceManager.addService(Context.LU_SERVICE, new LuTestService(context));
traceEnd();
}
- 将manager与service进行注册,在
frameworks/base/core/java/android/app/SystemServiceRegistry.java
中添加代码
//一定要记得导包
import com.my.service.ITestService;
import com.my.service.TestServiceManager;
final class SystemServiceRegistry {
static {
...
...
//注册我们自己的service与manager的联系
registerService(Context.LU_SERVICE, TestServiceManager.class,
new CachedServiceFetcher<TestServiceManager>() {
@Override
public TestServiceManager createService(ContextImpl ctx) throws ServiceNotFoundException {
IBinder b = ServiceManager.getServiceOrThrow(Context.LU_SERVICE);
ITestService service = ITestService.Stub.asInterface(b);
return new TestServiceManager(ctx, service);
}});
}
...
}
通过上面的配置,我们即将自定义的
TestService
添加到系统的SystemServer
中管理,并通过SystemServiceRegistry
注册了与TestServiceManager
的关联
四、给服务添加selinux权限
- 修改
system/sepolicy/private/service_contexts
...
...
window u:object_r:window_service:s0
#新增这一行 这个lu_service对应Context中添加的LU_SERVICE
lu_service u:object_r:lu_service:s0
* u:object_r:default_android_service:s0
- 修改
system/sepolicy/public/service.te
...
...
type window_service, system_api_service, system_server_service, service_manager_type;
# 最后一行增加,可以把上面一行复制下来,把window——service改成lu_service即可
type lu_service, system_api_service, system_server_service, service_manager_type;
五、编译运行
- 由于增加了新的模块
mytest
,先执行
#cd到源码根目录 可以直接执行 croot
make update-api
- 进行全编译
#cd到源码根目录 可以直接执行 croot
make -j16
- 使用新生成的img进行升级
- 重新启动后,通过
adb service list
查看是否有我们的lu_service
六、在客户端中使用
- 在编译成功后,可以在
out/target/product/xxxxx/system/framework
下看到我们的mytest.jar
把这个jar包导入到app中, - 在activity中使用
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//"lu_service"是之前在Context中定义的LU_SERVICE
TestServiceManager manager = (TestServiceManager) getSystemService("lu_service");
Log.d("MainActivity", manager.sayHello());
}
}
总结
- 添加系统服务的关键,是搞清楚
aidl
,manager
,service
之间的关系, - 首次编译系统升级成功后,后续修改:
修改了
TestService.java
,在frameworks/base/services/
下执行mm
,新生成的out/target/product/xxxxx/system/framework/services.jar
通过adb push
到手机系统目录/system/framework
目录下修改了
aidl
和manager
,在frameworks/base/mytest/
下执行mm
, 将out/target/product/xxxxx/system/framework/mytest.jar
通过adb push
到手机系统目录/system/framework
目录下,并导入到需要使用的客户端工程