註意:本文基於 Android 11 進行分析 Qidi 2023.11.28 (MarkDown & Haroopad) 0. 簡介 Android RO (Resource Overlay) 機制 Overlay 實現的效果正如其字面意思,就是“在原有效果的基礎上再疊加一些效果”。 Androi ...
註意:本文基於 Android 11 進行分析
Qidi 2023.11.28 (MarkDown & Haroopad)
0. 簡介 Android RO (Resource Overlay) 機制
Overlay 實現的效果正如其字面意思,就是“在原有效果的基礎上再疊加一些效果”。
Android 提供了兩種實現方式:
- 編譯時:https://source.android.com/docs/setup/create/new-device#use-resource-overlays
- 運行時:https://source.android.com/docs/core/runtime/rros
通過 RO 機制,我們就可以將自己編寫的 Java 服務在系統啟動時運行起來。
1. 實現自定義 Java 服務
假設我們要實現的自定義服務名叫 myService
,為了使它可以正常被 CarAudioService
拉起,需要在myService
的 AndroidManifest.xml
中添加 directBootAware
屬性:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
coreApp="true"
tools:ignore="MissingVersion"
package="com.your_company_name.myService"
android:sharedUserId="android.uid.system">
......
<application
android:allowBackup="true"
tools:ignore="All"
android:label="myService"
android:persistent="true"
android:directBootAware="true"
android:supportsRtl="true" >
<service
android:name=".myService"
android:singleUser="true"
android:exported="true"
android:permission="android.car.permission.CAR_CONTROL_AUDIO_SETTINGS"
tools:ignore="All"
android:enabled="true">
<intent-filter>
<action android:name="com.your_company_name.myService"/>
</intent-filter>
</service>
<!-- functionalities of myService depend on calls to below libraries -->
<uses-library
android:name="com.your_company_name.audio"
android:required="true"/>
<uses-library
android:name="com.your_company_name.effect"
android:required="true"/>
</application>
</manifest>
隨後實現 myService
代碼,和實現普通 service 沒有區別,示例 vendor/your_company_name/packages/src/com/your_company_name/myService.java
如下:
package com.your_company_name.myService;
// some dependent packages
import android.util.Log;
import xxxx;
public class myService extends Service {
private static final String TAG = "myService";
......
@Override
public IBinder onBind(Intent intent) {
// return a binder object to caller
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
@Override
public void onCreate() {
Log.i(TAG, "Creating myService...");
super.onCreate();
// creating and initializing myService
}
// implementation of some myService methods here
......
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
}
}
如此,自定義服務 myService
就編寫完成了。接下來我們還要做一些改動,讓 myService
在系統啟動時能自動被 CarAudioService
拉起。
2. 對 CarAudioService 配置文件進行編譯時 Overlay
為 CarAudioService
新建 Overlay 配置文件device/your_company_name/qcom/lunch_target_name/overlay/packages/services/Car/service/res/values/config.xml
:
<resources>
<!-- The services that needs to be started earlier in the boot sequence and in particular order.
Every item in this array contains a flatten component name of a service that needs to be
started and a list of parameters after hashtag symbol. Here's the format:
<item>com.bar.foo/.Service#bind={bind|start|startForeground},user={all|system|foreground},
trigger={asap,userUnlocked}</item>
bind: bind - start service with Context#bindService
start - start service with Context#startService
startForeground - start service with Context#startForegroundService
If service was bound it will be restarted unless it is constantly crashing.
The default value is 'start'
user: all - the service will be bound/started for system and all foreground users
system - the service will be started/bound only for system user (u0)
foreground - the service will be bound/started only for foreground users
The default value is 'all'
trigger: indicates when the service needs to be started/bound
asap - the service might be bound when user is not fully loaded, be careful with
this value, the service also needs to have directBootAware flag set to true
userUnlocked - start service when user unlocked the device
The default value is 'userUnlocked'
If the service bound/started for foreground user it will be unbound/stopped when user
is no longer foreground.
-->
<string-array translatable="false" name="config_earlyStartupServices">
<item>com.your_company_name.myService/.myService#bind=start,user=system,trigger=asap</item>
</string-array>
</resources>
然後在 makefile 中將上述 overlay 文件路徑添加到環境變數 PRODUCT_PACKAGE_OVERLAYS
中。以我使用的代碼環境為例,在 device/your_company_name/qcom/lunch_target_name/lunch_target_name.mk
中添加以下語句(當然你也可以添加到別的 makefile 中,比如 device.mk):
PRODUCT_PACKAGE_OVERLAYS += device/your_company_name/qcom/$(TARGET_PRODUCT)/overlay
至此,所有需要的改動都已完成。接下來只要確保編譯通過,並燒寫到板子上,就能在開機日誌中看到 myService
被拉起來的列印了。