第一步需要在frameworks/base/core/java/android目录下创建自己的模块
我模块所在目录:frameworks/base/core/java/android/custom,创建了一个custom的模块,在这里直接创建一个IKeepAliveManager.aidl
文件和一个KeepAliveManager.java
文件作为客户端,具体代码如下:
// IKeepAliveManager.aidl
package android.custom;
interface IKeepAliveManager {
boolean killApplicationByPackage(String packageName);
boolean keepAliveApplicationByPackage(String packageName);
void systemReady();
}
一共三个方法,一个通过包名主动杀进程。一个通过包名保活进程。一个systemReady
方法代表系统启动完成,aidl文件会在服务端去实现,客户端主要的目的是获取服务端提供的接口,进行访问
// KeepAliveManager.java
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.custom;
import android.content.Context;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
import android.util.Singleton;
public class KeepAliveManager {
private static String TAG = "KeepAliveManager";
private final Context mContext;
public KeepAliveManager(Context context){
mContext = context;
Log.e(TAG,"this:"+ mContext +",looper:"+ Looper.myLooper()+",mainLooper:"+context.getMainLooper());
}
/**
* @hide getService
* @return KeepAliveManagerService
* 获取服务端对象
*/
public static IKeepAliveManager getService() {
return IKeepAliveManagerSingleton.get();
}
// 杀掉进程
public boolean killApplicationByPackage(String packageName){
try {
return getService().killApplicationByPackage(packageName);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
// 保活进程
public boolean keepAliveApplicationByPackage(String packageName){
try {
return getService().keepAliveApplicationByPackage(packageName);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
// 获取服务端对象
private static final Singleton<IKeepAliveManager> IKeepAliveManagerSingleton = new Singleton<IKeepAliveManager>() {
@Override
protected IKeepAliveManager create() {
IBinder keepService = ServiceManager.getService(Context.APPLICATION_KEEP_ALIVE);
return IKeepAliveManager.Stub.asInterface(keepService);
}
};
}
IKeepAliveManager.Stub.Proxy(obj)
返回的一个IKeepAliveManager代理对象。IKeepAliveManager
和服务端的IBinder对象,拿到服务端代理对象,注意IKeepAliveManager
是从aidl生成的java文件第二步引入模块到android.bp文件中,让其参与编译
在frameworks/base/core/java/Android.bp引入自己的模块,添加如下代码
+filegroup {
+ name: "keep-alive-custom",
+ srcs: [
+ "android/custom/*.java",
+ "android/custom/*.aidl",
+ ],
+ visibility: ["//visibility:public"],
+}
...
每个模块都有自己的名称,可用于被依赖加载,这里的模块名叫keep-alive-custom
,srcs代表要加载的文件,visibility代表权限控制,希望哪些模块可以用到我们自己创建的模块,这样就可以了
第三步开始编译,让aidl生成java文件
只有编译后产生java文件,服务端才能访问到并实现aidl的产物,所以这个步骤很关键,原理是要将aidl编译存入到out/soong/.intermediates/frameworks/base/api/frameworks-base-api-current.txt/gen/current.txt和out/soong/.intermediates/frameworks/base/api-stubs-docs-non-updatable/android_common/metalava/api-stubs-docs-non-updatable_api.txt下面,才能访问对应的java文件,编译命令如下:
那为什么会有aidl对应的java文件产生呢?因为可以通过aidl命令编译出来,不过aosp整编的时候替我们做了,手动编译aidl java文件命令如下:
不出意外的话就出意外了,通过make -j16
整编会报错,原因是因为需要update-api,将AIDL内容输出到current.txt下面,报错如下:
可以看到生成了out/soong/.intermediates/frameworks/base/api-stubs-docs-non-updatable/android_common/metalava/api-stubs-docs-non-updatable_api.txt,就是需要将该aidl内容输出到该文件中
可以执行:m api-stubs-docs-non-updatable-update-current-api
这个命令即可,aidl内容就会输出到current.txt和api-stubs-docs-non-updatable_api.txt了
看一眼frameworks/base/core/api/current.txt是否生成aidl内容,已经生成的内容如下:
这样就ok了
第四步去掉编译时的代码检查,不然也会编译不过
因为编译时会检查代码的规范性,比如必须声明为systemApi等,@hide等,如果不规范则会报错且无法编译通过,如果发现很多代码的error,如下图
解决这个问题可以添加如下代码,移除编译时的规范检查,frameworks/base/Android.bp文件下,添加如下代码
// TODO(b/145644363): move this to under StubLibraries.bp or ApiDocs.bp
metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) " +
"--hide-package com.android.server " +
"--hide-package android.audio.policy.configuration.V7_0 " +
"--error UnhiddenSystemApi " +
"--hide RequiresPermission " +
"--hide CallbackInterface " +
"--hide MissingPermission --hide BroadcastBehavior " +
"--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
"--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo " +
"--error NoSettingsProvider " +
"--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.* " +
"--api-lint-ignore-prefix android.icu. " +
"--api-lint-ignore-prefix java. " +
"--api-lint-ignore-prefix junit. " +
**"--api-lint-ignore-prefix android.custom. " +**
"--api-lint-ignore-prefix org. "
添加"--api-lint-ignore-prefix android.custom. " +
即可,那为什么是android.custom,而不是完整的包名呢?原因是因为我是frameworks/base/core/java/android下创建的模块,可以直接通过android.xxx访问,不出意外的话模块下所有文件的包名就是package android.custom;
第五步,aidl对应的java文件已经生产,注释的文件也可以恢复了,不会报错了
服务端文件创建和实现
创建如下keepalive目录:frameworks/base/services/core/java/com/android/server/keepalive,并创建KeepAliveManagerService.java
,这里就可以写自己服务端的逻辑了
目录结构如下
然后让其继承IKeepAliveManager.Stub即可,具体代码如下
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.keepalive;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.custom.IKeepAliveManager;
import android.os.Binder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.R;
import com.android.server.am.ActivityManagerService;
import com.android.server.am.ProcessList;
import com.android.server.am.ProcessRecord;
import com.android.server.am.ProcessStateRecord;
import com.android.server.wm.WindowManagerService;
import java.util.Arrays;
import java.util.List;
public class KeepAliveManagerService extends IKeepAliveManager.Stub {
private static final String TAG = "KeepAliveManagerService leilei";
private final Context mContext;
private final ActivityManagerService mActivityManagerService;
private WindowManagerService mWindowManagerService;
private boolean mSystemReady = false;
public KeepAliveManagerService(Context ctx, ActivityManagerService ams){
this.mContext = ctx;
this.mActivityManagerService = ams;
}
public void setWindowManagerService(WindowManagerService wms) {
this.mWindowManagerService = wms;
}
/**
* killApplicationByPackage:杀死进程的逻辑
* @return
* @throws RemoteException
* ActivityManager: Force stopping com.android.documentsui appid=10040 user=0: from pid 586
* ActivityManager: Killing 2315:com.android.documentsui/u0a40 (adj 700): stop com.android.documentsui due to from pid 586
* libprocessgroup: Successfully killed process cgroup uid 10040 pid 2315 in 0ms
* ActivityTaskManager: Force removing ActivityRecord{e83ba41 u0 com.android.documentsui/.files.FilesActivity t27 f}}: app died, no saved state
*/
@Override
public boolean killApplicationByPackage(String packageName) throws RemoteException {
Log.d(TAG, "killApplicationByPackage.packageName:"+packageName);
if (TextUtils.isEmpty(packageName) || mActivityManagerService == null
|| mContext == null|| !mSystemReady){
return false;
}
int curCallingUid = Binder.getCallingUid();
return killApplicationByPackage(packageName,curCallingUid);
}
// 保护进程的逻辑
@Override
public boolean keepAliveApplicationByPackage(String packageName) throws RemoteException {
Log.d(TAG, "keepAliveApplicationByPackage.packageName:"+packageName);
if (TextUtils.isEmpty(packageName) || mActivityManagerService == null
|| mContext == null || !mSystemReady){
return false;
}
int curCallingUid = Binder.getCallingUid();
return keepAliveApplicationByPackage(packageName,curCallingUid);
}
// 保护进程的具体实现
private boolean keepAliveApplicationByPackage(String pkgName,int CallingUid){
final long callingId = Binder.clearCallingIdentity();
Log.d(TAG, "keepAliveApplicationByPackage.callUid:"+CallingUid);
// 获取保活名单
List<String> keepAliveWhiteList = Arrays.asList(mContext.getResources().getStringArray(
R.array.config_keepAlivePackages));
Log.d(TAG, "keepAliveApplicationByPackage.keepAliveWhiteList:"+ Arrays.toString(
keepAliveWhiteList.toArray()));
if (!Arrays.toString(keepAliveWhiteList.toArray()).contains(pkgName)){
Log.e(TAG, "keepAliveApplicationByPackage this app not in white list");
return false;
}
// 获取已经存在的的ProcessRecord集合
ProcessList.MyProcessMap processRecordList =
mActivityManagerService.getProcessList().getProcessNames();
PackageManager packageManager = mContext.getPackageManager();
ApplicationInfo appInfo = null;
try {
appInfo = packageManager.getApplicationInfo(pkgName,0);
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(e);
}
ProcessRecord curRecord = processRecordList.get(pkgName, appInfo.uid);
if (curRecord != null){
ProcessStateRecord psr = curRecord.mState;
Log.d(TAG, "keepAliveApplicationByPackage getAdjType:"+psr.getAdjType());
Log.d(TAG, "keepAliveApplicationByPackage getCurAdj:"+psr.getCurAdj());
Log.d(TAG, "keepAliveApplicationByPackage isPersistent:"+curRecord.isPersistent());
if (curRecord.isPersistent() && psr.getCurAdj() > ProcessList.PERSISTENT_PROC_ADJ){
psr.setMaxAdj(ProcessList.PERSISTENT_PROC_ADJ);
psr.setCurAdj(ProcessList.PERSISTENT_PROC_ADJ);
psr.setCurRawAdj(ProcessList.PERSISTENT_PROC_ADJ);
psr.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_PERSISTENT);
}else if (psr.getCurAdj() > ProcessList.FOREGROUND_APP_ADJ){
psr.setMaxAdj(ProcessList.FOREGROUND_APP_ADJ);
psr.setCurAdj(ProcessList.FOREGROUND_APP_ADJ);
psr.setCurRawAdj(ProcessList.FOREGROUND_APP_ADJ);
psr.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_TOP);
}
if (psr.isCached() || psr.isEmpty()){
psr.setCached(false);
psr.setEmpty(false);
}
return true;
}
Binder.restoreCallingIdentity(callingId);
return false;
}
// 杀死进程的具体实现
private boolean killApplicationByPackage(String pkgName,int CallingUid){
// 清理calling进程,使得kill操作都在system_server进程,拥有较高的权限
final long callingId = Binder.clearCallingIdentity();
// 获取AMS中的ProcessList,目前只有在AMS中才存在该实例
ProcessList processList = mActivityManagerService.getProcessList();
// 获取正在运行的进程
List<ActivityManager.RunningAppProcessInfo> runningAppProcesses =
mActivityManagerService.getRunningAppProcesses();
// 遍历正在运行的进程
for (ActivityManager.RunningAppProcessInfo runningApp:runningAppProcesses){
Log.d(TAG, "killApplicationByPackage runningApp:"+runningApp+",runningApp.uid:"+runningApp.uid);
// 找到我们想要kill的进程
if (TextUtils.equals(runningApp.processName,pkgName)){
// 如果进程的ProcessRecord存在
ProcessRecord processRecord = processList.getProcessNames().get(pkgName, runningApp.uid);
if (processRecord != null){
Log.d(TAG, "killApplicationByPackage: processRecord:"+processRecord);
// 设置进程的优先级为empty,让其更容易被kill
ProcessList.setOomAdj(runningApp.pid,runningApp.uid,1000);
// 设置为非持久进程,如果为false,那么被kill后无法自启
//processRecord.setPersistent(false);
// forceStopPackage无法kill持久进程(系统进程都带有持久进程的flag),主动调用该方法,evenPersistent为false,所以是可以杀掉系统进程的
// 不过会很快就被自启动
mActivityManagerService.forceStopPackage(pkgName, UserHandle.USER_ALL);
// 在杀一次,如果是持久进程,会被自启动
//mActivityManagerService.mInternal.killProcess(pkgName,runningApp.uid,"killApplicationByPackage");
//processRecord.appDied("killApplicationByPackage");
// 自动后这个返回的false
return processRecord.isKilled();
}
}
}
Binder.restoreCallingIdentity(callingId);
return false;
}
// 手机系统启动完成
@Override
public void systemReady() throws RemoteException {
Log.d(TAG, "systemReady:");
mSystemReady = true;
}
}
这里不用添加到android.bp文件中,因为服务端的文件都需要new出对象来使用,并且和是在同一个包下(base/services),SystemServer可通过包名访问该服务端对象。而客户端需要跨进程在frameworks来回穿梭中,故需要添加到android.bp文件中
服务端上下文声明
因为我们使用该系统服务时,需要通过mContext.getSystemService(Context.*APPLICATION_KEEP_ALIVE*);
来访问KeepAliveManager,所以需要在Context中添加一个自己的访问名称,当然也可以不用添加,直接传入字符串即可—下文会介绍如何注册到getSystemService
中,Context如下代码
/**
* application keep alive
* @hide
*/
public static final String APPLICATION_KEEP_ALIVE = "keep_alive";
增加@hide代表外部不可访问,例如app等无法访问,但系统进程可访问—声明了system.uid的进程,也就是系统api,仅供系统访问和使用,权限控制
客户端注册到getSystemService中—注意是客户端
因为外部需要通过mContext.getSystemService(Context.*APPLICATION_KEEP_ALIVE*);
来获取对象和使用系统服务,所以需要主持才能获取到,如下代码所示
frameworks/base/core/java/android/app/SystemServiceRegistry.java
@SystemApi
public final class SystemServiceRegistry {
static {
//CHECKSTYLE:OFF IndentationCheck
registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,
new CachedServiceFetcher<AccessibilityManager>() {
@Override
public AccessibilityManager createService(ContextImpl ctx) {
return AccessibilityManager.getInstance(ctx);
}});
registerService(Context.CAPTIONING_SERVICE, CaptioningManager.class,
new CachedServiceFetcher<CaptioningManager>() {
@Override
public CaptioningManager createService(ContextImpl ctx) {
return new CaptioningManager(ctx);
}});
registerService(Context.ACCOUNT_SERVICE, AccountManager.class,
new CachedServiceFetcher<AccountManager>() {
@Override
public AccountManager createService(ContextImpl ctx) throws ServiceNotFoundException {
IBinder b = ServiceManager.getServiceOrThrow(Context.ACCOUNT_SERVICE);
IAccountManager service = IAccountManager.Stub.asInterface(b);
return new AccountManager(ctx, service);
}});
registerService(Context.ACTIVITY_SERVICE, ActivityManager.class,
new CachedServiceFetcher<ActivityManager>() {
@Override
public ActivityManager createService(ContextImpl ctx) {
return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
}});
registerService(Context.ACTIVITY_TASK_SERVICE, ActivityTaskManager.class,
new CachedServiceFetcher<ActivityTaskManager>() {
@Override
public ActivityTaskManager createService(ContextImpl ctx) {
return ActivityTaskManager.getInstance();
}});
...
**registerService(Context.APPLICATION_KEEP_ALIVE, KeepAliveManager.class,
new CachedServiceFetcher<KeepAliveManager>() {
@Override
public KeepAliveManager createService(ContextImpl ctx)
throws ServiceNotFoundException{
return new KeepAliveManager(ctx);
}
});**
}
}
只需要在注册表中依葫芦画瓢调用registerService注册即可,第一个参数就是名称,第二个参数就是客户端对象,第三个参数就是创建客户端。这里为什么是创建的客户端呢?因为不能将服务端内容暴露给外部(用户),用户只能调用客户端(KeepAliveManager
),而具体逻辑由服务端来处理
服务端对象创建和启动时机—将服务端IBinder对象保存到ServiceManager统一管理