6.7 自定义 framwork service

277 阅读5分钟

6.7 自定义 framwork service

  1. 自定义 aidl 接口 IBeanManager.aidl
  2. android.bp 中加入编译路径
  3. 自定义 server 端 BeanManagerService.java —>[Context.java](<http://Context.java>) 中添加服务名 —> [SystemServer.java](<http://SystemServer.java>) 启动服务
  4. 自定义 client 端调用 [BeanManager.java](<http://BeanManager.java>)
  5. app 使用 framework 的接口

binder 数据传递

Binder默认可以传输基本类型的数据,如果要传递类对象,则这个类需要实现序列化。我们先定义一个序列化的自定义类型,用于binder间参数传递。主要分为两步:

  1. 创建自定义类型,实现Parcelable接口
// frameworks/base/core/java/android/bean/Person.java
package android.bean;

import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;

public final class Person implements Parcelable {
    // 注意定义成员变量的顺序,后面读取和写入都必须按这个顺序
    private String mName;
    private int mAge;

    public Person() {
    }

    public Person(@NonNull String name, int age) {
        this.mName = name;
        this.mAge = age;
    }

    private Person(@NonNull Parcel in) {
        // 注意定义成员变量的顺序
        this.mName = in.readString();
        this.mAge = in.readInt();
    }

    @NonNull
    public String getName() {
        return mName;
    }

    public int getAge() {
        return mAge;
    }

    public void setName(@NonNull String name) {
        mName = name;
    }

    public void setAge(int age) {
        mAge = age;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    // 
    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        // 注意定义成员变量的顺序
        dest.writeString(this.mName);
        dest.writeInt(this.mAge);
    }

    public void readFromParcel(@NonNull Parcel source) {
        // 注意定义成员变量的顺序
        this.mName = source.readString();
        this.mAge = source.readInt();
    }

    @NonNull
    public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {
        @Override
        public Person createFromParcel(@NonNull Parcel source) {
            return new Person(source);
        }

        @Override
        public Person[] newArray(int size) {
            return new Person[size];
        }
    };
}
  1. 创建aidl文件,如下:
// frameworks/base/core/java/android/bean/Person.aidl
package android.bean;

parcelable Person;

这样就定义好了一个自定义对象类型,该类型可以通过binder传递,下面我们会有例子说明。

自定义服务

接下来我们定义自定义服务,首先要定义一个aidl文件来定义我们的接口,这个aidl文件编译后会生成binder的客户端服务端接口,我们需要实现客户端和服务端。

// bean为新建的文件夹,用于存放自定义服务
// frameworks/base/core/java/android/bean/IBeanManager.aidl
package android.bean;

import android.bean.Person;

/**
 * {@hide}
 */
interface IBeanManager {
    void sayHello(String words);
    // 自定义类型前面必须加上in或者out或者inout标识符
    // in:    表示参数只能由客户端传递到服务端,基本类型默认只支持in修饰符
    // out:   表示参数只能由服务端传递到客户端,服务端如果修改了参数对象的值
    //        那么客户端的值也会改变,但是服务端无法读取客户端对象的值
    // inout: 表示参数可以双向传递
    void sayHelloTo(in Person person, String words);
}

编译 aidl ,由于下面的Android.bp已经包含了该目录下几乎所有aidl文件和java文件的编译,所以我们不需要再将自定义的BeanManager.aidl添加到编译环境。

// frameworks/base/core/java/Android.bp
filegroup {
    name: "framework-core-sources",
    srcs: [                                                                                                                                                                                                                     
        "**/*.java",
        "**/*.aidl",
    ],  
    exclude_srcs: [
        // Remove election toolbar code from build time
        "android/service/selectiontoolbar/*.aidl",
        "android/service/selectiontoolbar/*.java",
        "android/view/selectiontoolbar/*.aidl",
        "android/view/selectiontoolbar/*.java",
        "com/android/internal/widget/floatingtoolbar/RemoteFloatingToolbarPopup.java",
    ],
    visibility: ["//frameworks/base"],
}

用以下命令编译aidl

source build/envsetup.sh

lunch sdk_car_x86_64-userdebug// 这里选择自己的项目

make update-api

make framework -j16

编译完成后,可以看到out下已经生成了binder相关的文件

Untitled

Server 端实现

cd frameworks/base

mkdir -p services/core/java/com/android/server/bean/

新建BeanManagerService.java

编译配置同上,不需要再单独加到编译配置中。

// frameworks/base/services/core/java/com/android/server/bean/BeanManagerService.java
package com.android.server.bean;

import android.bean.IBeanManager;
import android.bean.Person;
import android.content.Context;
import android.util.Log;

import com.android.server.SystemService;
import com.android.server.Watchdog;

public class BeanManagerService extends IBeanManager.Stub
        implements Watchdog.Monitor {

    static final String TAG = "BeanManagerService";

    final Context mContext;

    public BeanManagerService(Context systemContext) {
        Log.d(TAG, "BeanManagerService");
        mContext = systemContext;
        Watchdog.getInstance().addMonitor(this);
    }

    @Override
    public void sayHello(String words) {
        Log.d(TAG, " sayHello : " + words);
    }

    @Override
    public void sayHelloTo(Person person, String words) {
        Log.d(TAG, " sayHello " + person.getName() + ": " + words);
    }

    public void systemReady() {
        Log.d(TAG, " systemReady ");
    }

    /** In this method we try to acquire our lock to make sure that we have not deadlocked */
    public void monitor() {
        synchronized (this) { }
    }

    // 这里我们定义了一个内部类继承SystemService,这样便于方便system_server统一管理启动服务
    // 系统启动时会回调所有继承了SystemService的类的函数,比如onStart,onBootPhase等等。
    public static final class Lifecycle extends SystemService {
        static final String TAG = "BeanManagerService.Lifecycle";

        private BeanManagerService mService;

        public Lifecycle(Context context) {
            super(context);
            // 在这里构造我们的BeanManagerService对象
            mService = new BeanManagerService(context);
        }

        @Override
        public void onStart() {
            Log.d(TAG, "onStart");
            // 这里最终调用ServiceManager.addService
            publishBinderService(Context.BEAN_SERVICE, mService);
        }

        @Override
        public void onBootPhase(int phase) {
            Log.d(TAG, "onBootPhase");
            if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
                mService.systemReady();
            }
        }
    }
}

要在 Context.java 中注册服务名称

/ frameworks/base/core/java/android/content/Context.java
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 77ca48a8ed1d..6ad91d26daf7 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3809,6 +3809,7 @@ public abstract class Context {
             ACCOUNT_SERVICE,
             ACTIVITY_SERVICE,
             ALARM_SERVICE,
+            BEAN_SERVICE,
             NOTIFICATION_SERVICE,
             ACCESSIBILITY_SERVICE,
             CAPTIONING_SERVICE,
@@ -4248,6 +4249,16 @@ public abstract class Context {
      */
     public static final String ACTIVITY_SERVICE = "activity";

+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.bean.BeanManager} for interacting with the global
+     * system state.
+     *
+     * @see #getSystemService(String)
+     * @see android.bean.BeanManager
+     */
+    public static final String BEAN_SERVICE = "bean";
+

systemServer 中开机启动服务:

// frameworks/base/services/java/com/android/server/SystemServer.java
// 系统启动时将服务分为几个类型,如下,我们可以根据自己的具体业务添加到对应的函数里面

try {
    t.traceBegin("StartServices");
    // 引导服务,必须
    startBootstrapServices(t);
    // 核心服务,必须
    startCoreServices(t);
    // 其他服务,一般放在这里面
    startOtherServices(t);
    // 定义在apexes中的服务apexes,有兴趣的可以看下源码
    startApexServices(t);
} catch (Throwable ex) {
    Slog.e("System", "******************************************");
    Slog.e("System", "************ Failure starting system services", ex);
    throw ex;
} finally {
    t.traceEnd(); // StartServices
}

--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -110,6 +110,7 @@ import com.android.server.appbinding.AppBindingService;
 import com.android.server.art.ArtManagerLocal;
 import com.android.server.attention.AttentionManagerService;
 import com.android.server.audio.AudioService;
+import com.android.server.bean.BeanManagerService;
 import com.android.server.biometrics.AuthService;
 import com.android.server.biometrics.BiometricService;
 import com.android.server.biometrics.sensors.face.FaceService;
@@ -1466,6 +1467,10 @@ public final class SystemServer implements Dumpable {
                 }
             }, SECONDARY_ZYGOTE_PRELOAD);

+            t.traceBegin("BeanManagerService");
             // 这里最终会通过反射的方法调用BeanManagerService.Lifecycle的构造函数
+            mSystemServiceManager.startService(BeanManagerService.Lifecycle.class);
+            t.traceEnd();
+
             t.traceBegin("StartKeyAttestationApplicationIdProviderService");
             ServiceManager.addService("sec_key_att_app_id_provider",
                     new KeyAttestationApplicationIdProviderService(context));

到此,服务的添加代码已经完成,我们编译验证一下:

// 因为Context.java中新增了BEAN_SERVICE的API,所以要先make update-api

source build/envsetup.sh

lunch sdk_car_x86_64-userdebug

make update-api -j16

make -j16

启动模拟器

emulator

这时发现服务无法启动,看日志有报错是关于avc的

Selinux权限问题,我们根据日志按规则添加:

adb pull /sys/fs/selinux/policy

adb logcat -b events -d | audit2allow -p policy

输出信息如下:

#============= system_server ==============
allow system_server default_android_service:service_manager add;

如果按照上面的提示应该是在system_server.te里添加allow system_server default_android_service:service_manager add;

但是这样会触发neverallow,正确的方法是为自定义的服务添加一个新的标签:

// vendor/zzh/sepolicy/private/service_contexts
bean                                  u:object_r:bean_service:s0

// vendor/zzh/sepolicy/public/service.te
type bean_service,       service_manager_type;

// vendor/zzh/sepolicy/public/system_server.te

allow system_server bean_service:service_manager add;

Client 端调用

Client调用实际上就是获取binder的代理对象,然后通过代理对象调用服务端的方法。在Android系统中,为了方便App使用系统服务的功能,一般都会给每个服务定义一个XXXManager,App可以通过Context.getSystemService(serviceName)来获取到经过包装的服务端的代理XXXManager。Context.getSystemService最终会调用到SystemServiceRegistry中的getSystemService。

接下来就是该定义BeanManager了