[AOSP] 如何创建系统服务

一. 简要

二. 客户端创建

  • 第一步需要在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);
            }
        };
    }
    
    • IKeepAliveManagerSingleton:通过该方法可以获取服务端对象的代理,为什么说是代理?因为AIDL的缘故,asInterface就是调用了IKeepAliveManager.Stub.Proxy(obj)返回的一个IKeepAliveManager代理对象。
      也就是通过ServiceManager拿到服务端的IBinder对象(服务端还没注册,且看下面的服务端创建流程,会注册到ServiceManager中),通过IKeepAliveManager和服务端的IBinder对象,拿到服务端代理对象,注意IKeepAliveManager是从aidl生成的java文件
    • keepAliveApplicationByPackage:通过包名保活进程,最后通过获取到的服务端对象,调用服务端的相同方法进行处理,客户端只是服务端的一个代理
    • killApplicationByPackage:通过包名杀死进程,最后通过获取到的服务端对象,调用服务端的相同方法进行处理,客户端只是服务端的一个代理
    • IKeepAliveManagerSingleton.get():获取到服务端对象
    • 至此客户端文件等都创建完成和准备完毕,但是服务端还没生成,还没编译,aidl对应的java文件也没生成,所以这些KeepAliveManager.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.txtout/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内容,已经生成的内容如下:

    [AOSP] 如何创建系统服务_第1张图片

    这样就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统一管理

你可能感兴趣的:(Android开发旅途,gitee,开发语言,android,aosp)