Android13添加自定义服务

AIDL

找个好位置(在android.os下创建kits目录),创建需要自定义的数据类与接口,注意官方API规范,否则编译会出错。

数据

用于传输的数据类须实现Parcelable接口(可借助Generator插件快速实现),Parcelable实现须为final类,如String可能为空的字段须加上如@NonNull的注解,须存在CREATOR常量;

#frameworks/base/core/java/android/os/kits/LocalMedia.java

import android.os.Parcel;
import android.os.Parcelable;
import android.annotation.NonNull;
import java.util.Objects;

public final class LocalMedia implements Parcelable {
	public final long id;
	public final @NonNull String path;
	public final @NonNull String name;
	public final long duration;
	public final int width;
	public final int height;
	public final long size;

	private LocalMedia(long id,@NonNull String path,@NonNull String name, long duration, int width, int height, long size) {
		this.id = id;
		this.path = path;
		this.name = name;
		this.duration = duration;
		this.width = width;
		this.height = height;
		this.size = size;
	}

	@Override
	public void writeToParcel(@NonNull Parcel dest, int flags) {
		dest.writeLong(this.id);
		dest.writeString(this.path);
		dest.writeString(this.name);
		dest.writeLong(this.duration);
		dest.writeInt(this.width);
		dest.writeInt(this.height);
		dest.writeLong(this.size);
	}

	private LocalMedia(@NonNull Parcel in) {
		this.id = in.readLong();
		this.path = in.readString();
		this.name = in.readString();
		this.duration = in.readLong();
		this.width = in.readInt();
		this.height = in.readInt();
		this.size = in.readLong();
	}

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

		@Override
		public LocalMedia[] newArray(int size) {
			return new LocalMedia[size];
		}
	};

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

	@Override
	public boolean equals(@NonNull Object o) {
		if (this == o) return true;
		if (o == null || getClass() != o.getClass()) return false;
		LocalMedia that = (LocalMedia) o;
		return id == that.id;
	}

	@Override
	public int hashCode() {
		return Objects.hash(id);
	}

	public long getId() {
		return id;
	}

	public @NonNull String getPath() {
		return path;
	}

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

	public long getDuration() {
		return duration;
	}

	public int getWidth() {
		return width;
	}

	public int getHeight() {
		return height;
	}

	public long getSize() {
		return size;
	}
}

如果有嵌套的数据类,同样注意非空注解,以及注意嵌套数据类的导入,可以使用全路径表示数据类;

#frameworks/base/core/java/android/os/kits/LocalMediaFolder.java

package android.os.kits;

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

import java.util.List;
import java.util.Objects;

public final class LocalMediaFolder implements Parcelable {
	public final long bucketId;
	public final @NonNull String name;
	public final @NonNull String coverPath;
	public final @NonNull String coverMimeType;
	public final int num;
	public final @NonNull List<android.os.kits.LocalMedia> data;

	private LocalMediaFolder(long bucketId, @NonNull String name, @NonNull String coverPath, @NonNull String coverMimeType, int num, @NonNull List<android.os.kits.LocalMedia> data) {
		this.bucketId = bucketId;
		this.name = name;
		this.coverPath = coverPath;
		this.coverMimeType = coverMimeType;
		this.num = num;
		this.data = data;
	}

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

	@Override
	public void writeToParcel(@NonNull Parcel dest, int flags) {
		dest.writeLong(this.bucketId);
		dest.writeString(this.name);
		dest.writeString(this.coverPath);
		dest.writeString(this.coverMimeType);
		dest.writeInt(this.num);
		dest.writeTypedList(this.data);
	}

	private LocalMediaFolder(@NonNull Parcel in) {
		this.bucketId = in.readLong();
		this.name = in.readString();
		this.coverPath = in.readString();
		this.coverMimeType = in.readString();
		this.num = in.readInt();
		this.data = in.createTypedArrayList(android.os.kits.LocalMedia.CREATOR);
	}

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

		@Override
		public LocalMediaFolder[] newArray(int size) {
			return new LocalMediaFolder[size];
		}
	};

	@Override
	public boolean equals(Object o) {
		if (this == o) return true;
		if (o == null || getClass() != o.getClass()) return false;
		LocalMediaFolder that = (LocalMediaFolder) o;
		return bucketId == that.bucketId;
	}

	@Override
	public int hashCode() {
		return Objects.hash(bucketId);
	}

	public long getBucketId() {
		return bucketId;
	}

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

	public @NonNull String getCoverPath() {
		return coverPath;
	}

	public @NonNull String getCoverMimeType() {
		return coverMimeType;
	}

	public int getNum() {
		return num;
	}

	public @NonNull List<android.os.kits.LocalMedia> getData() {
		return data;
	}
}

当然,也要注意package的路径,要使用系统相对路径。

数据类准备好后,需要编写相应的AIDL文件声明数据类的存在;

#frameworks/base/core/java/android/os/kits/LocalMedia.aidl
package android.os.kits;

parcelable LocalMedia;
#frameworks/base/core/java/android/os/kits/LocalMediaFolder.aidl
package android.os.kits;

parcelable LocalMediaFolder;

接口

接口不允许暴露,因此需要添加@hide注解,parcel数据类作为参数记得添加in标识;

#frameworks/base/core/java/android/os/kits/IKitManager.aidl

package android.os.kits;

import android.os.kits.LocalMediaFolder;
/**
* @hide
*/interface IKitManager {
	void loadSource(in LocalMediaFolder localMediaFolder);
	String getSystemInfo(int code);
}

编译

先行编译,使aidl生成对应的stub文件;
环境变量可使用lunch选择预置的版本,也可使用export临时改变相应的环境变量;

# 选择环境
source build/envsetup.sh
lunch aosp_oriole-userdebug
# 更新API
m update-api

服务

服务端

另外找个好位置(在com.android.server下创建kits目录下,显得合群些),创建服务类KitManagerService;
继承Stub类,编写LifeCycle(可用可不用,主要用于生命周期的联动);

#frameworks/base/services/core/java/com/android/server/kits/KitManagerService.java

package com.android.server.kits;

import android.os.kits.LocalMedia;
import android.os.kits.LocalMediaFolder;
import android.os.kits.IKitManager;
import android.content.Context;
import android.util.Log;

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

public class KitManagerService extends IKitManager.Stub implements Watchdog.Monitor{

	private final Context mContext;
	static KitManagerService sSelf = null;
	private static final boolean WATCHDOG_ENABLE = true;

	static final String TAG = "KitManagerService";
	
	public KitManagerService(Context context){
		Log.i(TAG,"kit start");

		sSelf = this;
		mContext = context;

		if (WATCHDOG_ENABLE) {
			Watchdog.getInstance().addMonitor(this);
		}
	}

	@Override
	public void loadSource(LocalMediaFolder localMediaFolder){
		Log.i(TAG,"loading folder");
	}

	@Override
	public String getSystemInfo(int code){
		return "";
	}

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


	@Override
	public void monitor() {

	}

	public static final class Lifecycle extends SystemService {

		private KitManagerService mService;

		public Lifecycle(Context context) {
			super(context);
			mService = new KitManagerService(context);
		}

		@Override
		public void onStart() {
			Log.d(TAG, "KitManager Lifecycle onStart");
			publishBinderService(Context.KIT_SERVICE, mService);
		}

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

Context常量

在Context.java中添加服务常量名;

--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3805,6 +3805,7 @@ public abstract class Context {
             POWER_SERVICE,
             //@hide: POWER_STATS_SERVICE,
             WINDOW_SERVICE,
+		    KIT_SERVICE,
             LAYOUT_INFLATER_SERVICE,
             ACCOUNT_SERVICE,
             ACTIVITY_SERVICE,
@@ -4166,6 +4167,13 @@ public abstract class Context {
      */
     public abstract @Nullable String getSystemServiceName(@NonNull Class<?> serviceClass);
 
+	/**
+	 * Use with {@link #getSystemService(String)} to retrieve a
+	 * {@link android.os.kits.KitManager} for controlling kits management,
+	 * @see android.os.kits.KitManager
+	 */
+	public static final String KIT_SERVICE = "kit";
+

启动服务

在SystemServer.java中选择一个阶段启动此服务;

#frameworks/base/services/java/com/android/server/SystemServer.java
            startBootstrapServices(t);
            startCoreServices(t);
            startOtherServices(t);
            startApexServices(t);

这里选择放到startOtherServices方法里启动;
需要生命周期的联动,则使用LifeCycle内部类启动;

--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -136,6 +136,7 @@ import com.android.server.incident.IncidentCompanionService;
 import com.android.server.input.InputManagerService;
 import com.android.server.inputmethod.InputMethodManagerService;
 import com.android.server.integrity.AppIntegrityManagerService;
+import com.android.server.kits.KitManagerService;
 import com.android.server.lights.LightsService;
 import com.android.server.locales.LocaleManagerService;
 import com.android.server.location.LocationManagerService;
@@ -1466,6 +1467,11 @@ public final class SystemServer implements Dumpable {
                 }
             }, SECONDARY_ZYGOTE_PRELOAD);
 
+            t.traceBegin("StartKitManagerService");
+//			mSystemServiceManager.startService(KitManagerService.Lifecycle.class);
+	        ServiceManager.addService(Context.KIT_SERVICE, new KitManagerService(context));
+	        t.traceEnd();
+
             t.traceBegin("StartKeyAttestationApplicationIdProviderService");
             ServiceManager.addService("sec_key_att_app_id_provider",
                     new KeyAttestationApplicationIdProviderService(context));

注册系统服务

为了可以使用Context.getSystemService方法获取到此服务,就要在SystemServiceRegistry注册一个全局的客户端KitManager;

--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -248,6 +248,8 @@ import com.android.internal.util.Preconditions;
 import java.util.Map;
 import java.util.Objects;
 
+import android.os.kits.KitManager;
+
@@ -332,6 +334,13 @@ public final class SystemServiceRegistry {
+		registerService(Context.KIT_SERVICE, KitManager.class, new CachedServiceFetcher<KitManager>() {
+			@Override
+			public KitManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+				return new KitManager();
+			}
+		});
+

客户端

其实是一个服务代理,以便外部引用;
同样注意各类注解规范;

#frameworks/base/core/java/android/os/kits/KitManager.java

package android.os.kits;

import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
import android.annotation.SystemService;
import android.compat.annotation.UnsupportedAppUsage;
import android.util.Singleton;
import android.annotation.Nullable;
import android.os.kits.LocalMediaFolder;

@SystemService(Context.KIT_SERVICE)
public class KitManager {
	private static final String TAG = "KitManager";

	/**
	 * @hide
	 */
	public KitManager(){
		Log.d(TAG, "kitmanager init");
	}

	/**
	 * @hide
	 */
	public static IKitManager getService() {
		return I_KIT_MANAGER_SINGLETON.get();
	}

	@UnsupportedAppUsage
	private static final Singleton<IKitManager> I_KIT_MANAGER_SINGLETON =
			new Singleton<IKitManager>() {
				@Override
				protected IKitManager create() {
					final IBinder b= ServiceManager.getService(Context.KIT_SERVICE);
					final IKitManager im=IKitManager.Stub.asInterface(b);
					return im;
				}
			};

	@Nullable
	public void loadSource(@Nullable LocalMediaFolder localMediaFolder){
		Log.d(TAG, "folder: ");
		if (getService() == null) {
			Log.w(TAG, "Kit mService is null");
			return;
		}
		try{
			getService().loadSource(localMediaFolder);
		} catch (RemoteException e) {
			throw e.rethrowFromSystemServer();
		}
	}

	@Nullable
	public String getSystemInfo(int code){
		Log.d(TAG, "getSysteminfo: ");
		if (getService() == null) {
			Log.w(TAG, "Kit mService is null");
			return "";
		}
		try{
			return getService().getSystemInfo(code);
		} catch (RemoteException e) {
			throw e.rethrowFromSystemServer();
		}
	}
}

权限

策略文件

以上的操作和十年前的版本大差不差,真正的差别在权限这里。
自Android6.0的动态权限开始,Android系统权限肉眼可见地步步收紧,针对广播、服务、文件存储、通知的各类权限进行全方位地限制,这种限制也体现到了AOSP层面上。
在这里与之相关的服务权限就是SELinux,也可以叫SEAndroid,在4.x版本就已经出现。
简而言之,要想在系统服务上动手脚,需要在SELinux的相关安全策略文件中进行安全声明,可以简单理解为另一种"白名单"。

在 Android 8.0 及更高版本中,政策位于 AOSP 中的以下位置:

  • system/sepolicy/public。其中包括所导出的用于供应商特定政策的政策。所有内容都会纳入 Android 8.0 兼容性基础架构。公共政策会保留在不同版本上,因此您可以在自定义政策的 /public 中添加任何内容。正因如此,可存放在 /public 中的政策类型的限制性更强。将此目录视为相应平台的已导出政策 API:处理 /system 与 /vendor 之间的接口的所有内容都位于这里。
  • system/sepolicy/private。包括系统映像正常运行所必需(但供应商映像政策应该不知道)的政策。
  • system/sepolicy/vendor。包括位于 /vendor 但存在于核心平台树(非设备特定目录)中的组件的相关政策。这是构建系统区分设备和全局组件的软件工件;从概念上讲,这是下述设备专用政策的一部分。
  • device/manufacturer/device-name/sepolicy。包含设备专用政策,以及对政策进行的设备自定义(在 Android 8.0 及更高版本中,该政策对应于供应商映像组件的相关政策)。

以上策略到本文所用的13.0系统时尚未有较大的变动,需要改动的策略文件大致如下:

sepolicy
    --prebuilts
        --28.0
        --32.0
        --33.0
            --private
                --service_contexts
                --untrust_app_all.te
                --system_server.te
            --public
                --service.te
    --private
        --service_contexts
        --untrust_app_all.te
        --system_server.te
    --public
        --service.te

即service.te、system_server.te、sevice_contexts、untrust_app_all.te这4个文件,以及相应版本(当前为33.0,即Android13系统)目录下的同名文件。
内容仿照其他服务即可,具体规则可参考自定义SELinux

策略声明

在public目录下的service.te中;

--- a/public/service.te
+++ b/public/service.te
@@ -59,6 +59,7 @@ type vrflinger_vsync_service,   service_manager_type;
 type accessibility_service, app_api_service, ephemeral_app_api_service, system_server_service, service_manager_type;
 type account_service, app_api_service, ephemeral_app_api_service, system_server_service, service_manager_type;
 type activity_service, app_api_service, ephemeral_app_api_service, system_server_service, service_manager_type;
+type kit_service, app_api_service, ephemeral_app_api_service, system_server_service, service_manager_type;
 type activity_task_service, app_api_service, ephemeral_app_api_service, system_server_service, service_manager_type;
 type adb_service, system_api_service, system_server_service, service_manager_type;
 type adservices_manager_service, system_api_service, system_server_service, service_manager_type;

在private目录下的untrusted_app_all.te中;

--- a/private/untrusted_app_all.te
+++ b/private/untrusted_app_all.te
@@ -89,6 +89,7 @@ allow untrusted_app_all servicemanager:service_manager list;
 
 allow untrusted_app_all audioserver_service:service_manager find;
 allow untrusted_app_all cameraserver_service:service_manager find;
+allow untrusted_app_all kit_service:service_manager find;
 allow untrusted_app_all drmserver_service:service_manager find;
 allow untrusted_app_all mediaserver_service:service_manager find;
 allow untrusted_app_all mediaextractor_service:service_manager find;

在private目录下的system_server.te中;

--- a/private/system_server.te
+++ b/private/system_server.te
@@ -892,6 +892,7 @@ allow system_server audioserver_service:service_manager find;
 allow system_server authorization_service:service_manager find;
 allow system_server batteryproperties_service:service_manager find;
 allow system_server cameraserver_service:service_manager find;
+allow system_server kit_service:service_manager find;
 allow system_server compos_service:service_manager find;
 allow system_server dataloader_manager_service:service_manager find;
 allow system_server dnsresolver_service:service_manager find;

在private目录下的service_contexts中;

--- a/private/service_contexts
+++ b/private/service_contexts
@@ -73,6 +73,7 @@ android.system.suspend.ISystemSuspend/default                        u:object_r:
 accessibility                             u:object_r:accessibility_service:s0
 account                                   u:object_r:account_service:s0
 activity                                  u:object_r:activity_service:s0
+kit                                      u:object_r:kit_service:s0
 activity_task                             u:object_r:activity_task_service:s0
 adb                                       u:object_r:adb_service:s0
 adservices_manager                        u:object_r:adservices_manager_service:s0

同样地,在prebuilts目录下也要修改相应的文件,例如service.te:

--- a/prebuilts/api/33.0/public/service.te
+++ b/prebuilts/api/33.0/public/service.te
@@ -59,6 +59,7 @@ type vrflinger_vsync_service,   service_manager_type;
 type accessibility_service, app_api_service, ephemeral_app_api_service, system_server_service, service_manager_type;
 type account_service, app_api_service, ephemeral_app_api_service, system_server_service, service_manager_type;
 type activity_service, app_api_service, ephemeral_app_api_service, system_server_service, service_manager_type;
+type kit_service, app_api_service, ephemeral_app_api_service, system_server_service, service_manager_type;
 type activity_task_service, app_api_service, ephemeral_app_api_service, system_server_service, service_manager_type;
 type adb_service, system_api_service, system_server_service, service_manager_type;
 type adservices_manager_service, system_api_service, system_server_service, service_manager_type;

依此类推。
唯一要注意的就是,同名策略文件里的内容先后顺序不要乱,否则编译时就会报错与外围策略文件不一致。

因为这里用的userdebug版本且调试用的真机为google的pixel,所以只在system下操作即可,理论上如果有特定的设备厂商,最好在vendor目录下进行策略文件的新建与修改。

工具

权限相关的日志可以用“avc”来查看;

adb logcat | grep avc

或者转储:

adb shell su root dmesg | grep 'avc: '

找到相关日志后可以配合audit2allow工具进行修改。

当然有时候在真机上调试出错或者反复重启,就会很难抓到相关日志;
最干脆地还是直接关闭SELinux的验证,而用adb命令修改重启后仍会恢复,修改system/core/init/selinux.cpp文件就是终极手段;

bool IsEnforcing() {
    if (ALLOW_PERMISSIVE_SELINUX) {
        return StatusFromProperty() == SELINUX_ENFORCING;
    }
    //return true;
    return false;
}

让其返回false即可。

另外,手动修改策略文件难免手抖,写个脚本工具用来代替这种重复性工作再合适不过了,正好编译AOSP的unbuntu系统往往自带python(MacOS也有,但两年前就不再支持编译AOSP了)。

selinux权限脚本在这里,简单的字符串拼接。

验证

最后编译刷机一气呵成;

m
adb reboot bootloader
fastboot flashall -w

编译时环境变量如下:

PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=13
TARGET_PRODUCT=aosp_oriole
TARGET_BUILD_VARIANT=userdebug
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm64
TARGET_ARCH_VARIANT=armv8-2a
TARGET_CPU_VARIANT=cortex-a55
TARGET_2ND_ARCH=arm
TARGET_2ND_ARCH_VARIANT=armv8-a
TARGET_2ND_CPU_VARIANT=generic
HOST_ARCH=x86_64
HOST_2ND_ARCH=x86
HOST_OS=linux
HOST_OS_EXTRA=Linux-5.4.0-150-generic-x86_64-Ubuntu-18.04.6-LTS
HOST_CROSS_OS=windows
HOST_CROSS_ARCH=x86
HOST_CROSS_2ND_ARCH=x86_64
HOST_BUILD_TYPE=release
BUILD_ID=TQ1A.221205.011
OUT_DIR=/home/xter/images

真机为pixel6,服务正常启动:
kit

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值