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,服务正常启动: