通常我们通过如下代码来获取systemservice
ActivityManager activityManager= (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
activityManager.getRunningAppProcesses();
复制代码
而getSystemService的相关实现是在ContextImpl:frameworks/base/core/java/android/app/ContextImpl.java,代码如下:
@Override
public Object getSystemService(String name) {
if (vmIncorrectContextUseEnabled()) {
// Check incorrect Context usage.
if (isUiComponent(name) && !isSelfOrOuterUiContext()) {
final String errorMessage = "Tried to access visual service "
+ SystemServiceRegistry.getSystemServiceClassName(name)
+ " from a non-visual Context:" + getOuterContext();
final String message = "Visual services, such as WindowManager, WallpaperService "
+ "or LayoutInflater should be accessed from Activity or other visual "
+ "Context. Use an Activity or a Context created with "
+ "Context#createWindowContext(int, Bundle), which are adjusted to "
+ "the configuration and visual bounds of an area on screen.";
final Exception exception = new IllegalAccessException(errorMessage);
StrictMode.onIncorrectContextUsed(message, exception);
Log.e(TAG, errorMessage + " " + message, exception);
}
}
return SystemServiceRegistry.getSystemService(this, name);
}
复制代码
可以看到最终是通过SystemServiceRegistry(frameworks/base/core/java/android/app/SystemServiceRegistry.java)的类来获取的,通过类名我们可以推断这里面一定有相关的注册订阅方法,进入后查看可以发现确实存在一个订阅函数:
private static <T> void registerService(@NonNull String serviceName,
@NonNull Class<T> serviceClass, @NonNull ServiceFetcher<T> serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
SYSTEM_SERVICE_CLASS_NAMES.put(serviceName, serviceClass.getSimpleName());
}
复制代码
并且在该类里,我们搜索对应的ActivityManager发现很多的Manager均在此内的静态代码块调用registerService进行了注册:
static {
......省略......
registerService(Context.ACTIVITY_SERVICE, ActivityManager.class,
new CachedServiceFetcher<ActivityManager>() {
@Override
public ActivityManager createService(ContextImpl ctx) {
return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
}});
}
......省略......
复制代码
可以看到我们最终get拿到的就是在这里订阅构造的ActivityManager,其实ActivityManager只是一个代理类,我们具体调用的实现还是依靠在ActivityManager里的Binder实现类Service提供的。
接下来我们自行实现一个SystemService,并且提供对应java包给应用进行调用。
编写AIDL文件
我们同样模仿ActivityManager将AIDL创建在frameworks/base/core/java/android/app/路径下即可
frameworks/base/core/java/android/app/yy/ITestService.aidl:
package android.app.yy;
/**
* @hide
* */
interface ITestService {
String todo();
}
复制代码
添加servicename
接着我们去Context里补充一下获取Manager时用的静态变量,类似于Context.ACTIVITY_SERVICE,我们在getSystemService里传入的和registerService就是通过这个变量作为key,来进行映射查找。
frameworks/base/core/java/android/content/Context.java:
public static final String TEST_SERVICE = "test";
/**
*另外Contex里还有个自定义注解@ServiceName,具体用在了Context.getSystemService的参数处做限制:
*public abstract @Nullable Object getSystemService(@ServiceName @NonNull String name);
*所以我们为了一致性和规范性,也在@ServiceName的定义处把我们添加的Service补充进去:
**/
/** @hide */
@StringDef(suffix = { "_SERVICE" }, value = {
//......省略......
TEST_SERVICE,
ACTIVITY_SERVICE,
//......省略......
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
复制代码
实现Service逻辑
因为已经编写了AIDL,即在编译后会生成对应的Binder代理类,所以我们可以先去集成Stub把Service逻辑编写了:
到frameworks/base/services/core/java/com/android/server/路径下,可以看到有个am目录,ams即在该路径下实现的,我们模仿在此同级创建我们的service路径即可:
frameworks/base/services/core/java/com/android/server/yy/TestService.java
package com.android.server.yy;
import android.content.Context;
import android.app.yy.ITestService;
public class TestService extends ITestService.Stub {
private final Context mContext;
public TestService(Context context) {
super();
mContext = context;
}
@Override
public String todo() {
return "调用todo成功";
}
}
复制代码
注册Service
注意这里是注册Service而非注册Manager,所有的Service(Binder实现类)均是在SystemServer进程进行创建,并注册到ServiceManager进程的,Android中所有的Binder交互均是通过Binder驱动去ServiceManager找到对应Service进行通讯,frameworks/base/services/java/com/android/server/SystemServer.java:
/**
* SystemServer的run里封装了三种启动不同类型service的函数,
* 例如AMS这些就是在startBootstrapServices里创建并注册的,
* 这里我们自定义的Service添加到startOtherServices里即可:
*/
private void run() {
// Start services.
try {
t.traceBegin("StartServices");
startBootstrapServices(t);
startCoreServices(t);
startOtherServices(t);
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
} finally {
t.traceEnd(); // StartServices
}
}
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
//......省略......
TestService testService=new TestService(context);
ServiceManager.addService(Context.TEST_SERVICE,testService);
//......省略......
}
复制代码
实现Manager
接下来我们即可实现对应Manager,以提供给应用方使用,Manager实现在和AIDL同一路径,
frameworks/base/core/java/android/app/yy/TestServiceManager.java
package android.app.yy;
import android.os.IBinder;
import android.os.ServiceManager;
import android.app.yy.ITestService;
import android.content.Context;
import android.os.RemoteException;
import android.compat.annotation.UnsupportedAppUsage;
import android.annotation.Nullable;
import android.os.ServiceManager.ServiceNotFoundException;
public class TestServiceManager {
private static TestServiceManager instance;
private final ITestService mService;
private Context mContext;
/**
* @hide
*/
private TestServiceManager(ITestService service,Context context) {
mService = service;
mContext=context;
}
/**
* @hide
*/
@UnsupportedAppUsage
public static TestServiceManager getInstance(@Nullable Context context) {
synchronized (TestServiceManager.class) {
if (instance == null) {
try {
instance = new TestServiceManager(ITestService.Stub
.asInterface(ServiceManager.getService(Context.TEST_SERVICE)),context);
} catch (ServiceNotFoundException e) {
throw new IllegalStateException(e);
}
}
return instance;
}
}
public @Nullable String todo() {
try {
return mService.todo();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
复制代码
可以看到实际上Manager只是Service的代理而已,而Service是通过ServiceManager.getService获取到的,通过的正是我们在SystemServer中addService传入的service_name。
Manager实现的注意事项
Android 高版本开始lint检测会对Manager有一定的限制,比如实现必须为单利,函数需要注解标识参数和返回值是否可为null,以及方法命名是否大小写(驼峰)语法规范等。一般是建议按照规范进行实现处理,如果是涉及到升级兼容的问题,可以根据编译日志提示,找到lint-baseline.txt编写规则进行忽略,具体的编写规则,会在报错后输出到api_lint_baseline.txt
注册Manager
接下来我们只需要像最开始分析的那样把Manager在SystemServiceRegistry内注册,用户即可通过getSystemService获取到Manager对象进行调用了。frameworks/base/core/java/android/app/SystemServiceRegistry.java:
static {
registerService(Context.TEST_SERVICE, TestServiceManager.class,
new CachedServiceFetcher<TestServiceManager>() {
@Override
public TestServiceManager createService(ContextImpl ctx) {
return TestServiceManager.getInstance(ctx);
}
});
}
复制代码
补充current.txt
最后因为规定修改了framework/base下的api需要更新current.txt,可以通过 make update-api会自动在framewrok/base/api下的current.txt中生成
SE权限
最后需要添加一下service的se权限,否则无法访问,一般是在device路径下找对应的te文件进行修改,这里路径每个项目不太一样,此处以android源码下mtk路径为例子:
device/mediatek/wembley-sepolicy/plat_public/service.te
# ==============================================
# MTK Policy Rule
# ==============================================
# System Server Services
# Other Services
type nvram_agent_service, service_manager_type;
#定义我们的service
type test_service, app_api_service,system_server_service,service_manager_type;
复制代码
device/mediatek/wembley-sepolicy/plat_private/service_contexts
# ==============================================
# MTK Policy Rule
# ==============================================
# System Server Services
# Other Services
NvRAMAgent u:object_r:nvram_agent_service:s0
memory_dumper u:object_r:mediaserver_service:s0
imsa u:object_r:radio_service:s0
mtkIms u:object_r:radio_service:s0
GbaService u:object_r:radio_service:s0
#定义我们的service
test u:object_r:test_service:s0
复制代码
使用
因为是自己创建的Service,SDK并没有该接口暴露,所以为了使用和编译,可以自己创建一个对应路径的Manager类编译成jar包提供给用户,例如:
创建android.app.yy路径,创建TestServiceManager.java,里面添加空方法todo,然后编译成jar包给用户。
用户使用:
/**
* 因为Lint检测,getSystemService传参会要求传Context的静态变量,如果想要通过传入Class,则需要高版本适配
* 所以可以通过@SuppressLint("WrongConstant")注解忽略
*/
@SuppressLint("WrongConstant")TestServiceManager manager= (TestServiceManager) getSystemService("test");
String ret=manager.todo();
复制代码