上一节完成了 BMP180 HAL 开发,现在再来继续后续内容:Bmp180Service 服务开发、APP 开发和处理 SEAndroid。
先到 frameworks/base/core/java/android/os 目录下新建 bmp180 目录,然后创建 Bmp180Manager.java、BMP180TemperatureAndPressure.java、BMP180TemperatureAndPressure.aidl 和 IBmp180Service.aidl。
APP 需要和 Framework 通信,因此使用了 aidl,APP 面向 Bmp180Manager 开放的接口编程。
先来定义 IBmp180Service.aidl。
IBmp180Service.aidl
package android.os.bmp180;
import android.os.bmp180.BMP180TemperatureAndPressure;
interface IBmp180Service {
boolean open();
void close();
void getTemperatureAndPressure(inout BMP180TemperatureAndPressure bMP180TemperatureAndPressure);
}
接着 IBmp180Service.aidl 中引用了 BMP180TemperatureAndPressure 结构,它定义在 BMP180TemperatureAndPressure.aidl 中。
BMP180TemperatureAndPressure.aidl
package android.os.bmp180;
parcelable BMP180TemperatureAndPressure;
BMP180TemperatureAndPressure.aidl 中可序列化的声明实际对应了 BMP180TemperatureAndPressure.java。
BMP180TemperatureAndPressure.java
package android.os.bmp180;
import android.os.Parcel;
import android.os.Parcelable;
public class BMP180TemperatureAndPressure implements Parcelable {
public static final Creator<BMP180TemperatureAndPressure> CREATOR = new Creator<BMP180TemperatureAndPressure>() {
@Override
public BMP180TemperatureAndPressure createFromParcel(Parcel in) {
return new BMP180TemperatureAndPressure(in);
}
@Override
public BMP180TemperatureAndPressure[] newArray(int size) {
return new BMP180TemperatureAndPressure[size];
}
};
private long temperature;
private long pressure;
public BMP180TemperatureAndPressure(long temperature, long pressure) {
this.temperature = temperature;
this.pressure = pressure;
}
public BMP180TemperatureAndPressure(Parcel in) {
temperature = in.readLong();
pressure = in.readLong();
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeLong(temperature);
out.writeLong(pressure);
}
public void readFromParcel(Parcel in) {
temperature = in.readLong();
pressure = in.readLong();
}
public long getTemperature() {
return temperature;
}
public void setTemperature(long temperature) {
this.temperature = temperature;
}
public long getPressure() {
return pressure;
}
public void setPressure(long pressure) {
this.pressure = pressure;
}
@Override
public int describeContents() {
return 0;
}
}
面向 APP 的前端 Bmp180Manager.java 通过 aidl 调用远程的 Framework Service。
Bmp180Manager.java
package android.os.bmp180;
import android.os.RemoteException;
import android.util.Log;
import android.os.bmp180.BMP180TemperatureAndPressure;
public class Bmp180Manager {
public static final String TAG = "Bmp180Manager";
private IBmp180Service mService;
public Bmp180Manager(IBmp180Service server) {
Log.d(TAG, "Bmp180Manager");
mService = server;
}
public boolean open(){
Log.d(TAG, "open");
try {
if (mService != null) {
return mService.open();
}
} catch (RemoteException e) {
e.printStackTrace();
}
return false;
}
public void close() {
Log.d(TAG, "close");
try {
if (mService != null) {
mService.close();
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void getTemperatureAndPressure(BMP180TemperatureAndPressure in){
Log.d(TAG, "getTemperatureAndPressure in=" + in);
try {
if (mService != null) {
Log.d(TAG, "getTemperatureAndPressure mService=" + mService);
mService.getTemperatureAndPressure(in);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
现在来看远端 Bmp180Service 实现。
frameworks/base/services/core/java/com/android/server/bmp180/Bmp180Service.java
package com.android.server.bmp180;
import android.hardware.bmp180.V1_0.IBmp180;
import android.os.RemoteException;
import android.util.Log;
import android.os.bmp180.IBmp180Service;
import android.os.bmp180.BMP180TemperatureAndPressure;
public class Bmp180Service extends IBmp180Service.Stub {
public static final String TAG = "Bmp180Service";
private IBmp180 halService;
public Bmp180Service(){
try {
halService = IBmp180.getService();
Log.d(TAG, "IBmp180 get service() halService=" + halService);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public boolean open(){
try {
Log.d(TAG, "open()");
return halService.open();
} catch (RemoteException e) {
e.printStackTrace();
}
return false;
}
public void close(){
try {
Log.d(TAG, "close()");
halService.close();
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void getTemperatureAndPressure(android.os.bmp180.BMP180TemperatureAndPressure bMP180TemperatureAndPressure){
try {
Log.d(TAG, "getTemperatureAndPressure(...)");
android.hardware.bmp180.V1_0.BMP180TemperatureAndPressure hardwareBMP180TemperatureAndPressure =
halService.getTemperatureAndPressure();
bMP180TemperatureAndPressure.setTemperature((long)(hardwareBMP180TemperatureAndPressure.temperature));
bMP180TemperatureAndPressure.setPressure((long)(hardwareBMP180TemperatureAndPressure.pressure));
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
frameworks/base/core/java/android/content/Context.java
public static final String BMP180_SERVICE = "bmp180";
frameworks/base/core/java/android/app/SystemServiceRegistry.java
......
import android.os.bmp180.Bmp180Manager;
import android.os.bmp180.IBmp180Service;
final class SystemServiceRegistry {
......
static {
......
// register for bmp180
registerService(Context.BMP180_SERVICE, Bmp180Manager.class, new CachedServiceFetcher<Bmp180Manager>() {
@Override
public Bmp180Manager createService(ContextImpl ctx) throws ServiceNotFoundException {
IBinder b = ServiceManager.getServiceOrThrow(Context.BMP180_SERVICE);
return new Bmp180Manager(IBmp180Service.Stub.asInterface(b));
}
});
}
......
}
frameworks/base/services/java/com/android/server/SystemServer.java
......
import com.android.server.bmp180.Bmp180Service;
......
public final class SystemServer {
......
private void startOtherServices() {
......
try {
Slog.i(TAG, "bmp180 Service");
ServiceManager.addService(Context.BMP180_SERVICE, new Bmp180Service());
} catch (Throwable e) {
reportWtf("starting Bmp180 service", e);
}
}
......
}
到这里整个 Framework 代码就算开发完成了,但是此时还要添加编译配置到 mk 等文件。
frameworks/base/Android.mk
LOCAL_SRC_FILES += \
......
core/java/android/os/bmp180/IBmp180Service.aidl \
frameworks/base/services/core/Android.mk
LOCAL_JAVA_LIBRARIES := \
......
android.hardware.bmp180-V1.0-java \
LOCAL_STATIC_JAVA_LIBRARIES := \
......
android.hardware.bmp180-V1.0-java \
修改了 mk 的位置,都可以 mm 编译一下,检查代码是否存在问题。
这里只贴出 MainActivity.java、activity_main.xml、AndroidManifest.xml 和 Android.mk。APP 界面很简单一个 btn 和 text,点击 btn 就会把传感器实时温度和压力读上来,显示到界面上。
MainActivity.java
package com.lhw.bmp180;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.bmp180.Bmp180Manager;
import android.os.bmp180.BMP180TemperatureAndPressure;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
private TextView mDisplayTextView;
private Button mReadBtn;
private Bmp180Manager mBmp180Manager;
private boolean isOpen = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDisplayTextView = findViewById(R.id.temp_and_press_textview);
mReadBtn = findViewById(R.id.read_btn);
mReadBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
BMP180TemperatureAndPressure bMP180TemperatureAndPressure = new BMP180TemperatureAndPressure(0L, 0L);
mBmp180Manager.getTemperatureAndPressure(bMP180TemperatureAndPressure);
mDisplayTextView.setText("Temp:" +
bMP180TemperatureAndPressure.getTemperature() / 10.0f +
"℃ Pressure:" +
bMP180TemperatureAndPressure.getPressure() + "Pa");
}
});
}
@Override
protected void onResume() {
super.onResume();
mBmp180Manager = (Bmp180Manager) getSystemService(Context.BMP180_SERVICE);
isOpen = mBmp180Manager.open();
Log.d("lhw", "isOpen=" + isOpen);
}
@Override
protected void onPause() {
super.onPause();
if (isOpen) mBmp180Manager.close();
}
}
activity_main.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/temp_and_press_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_margin="6dp"
android:text="Temp:--℃ Pressure:--Pa"
android:textColor="#696969"
android:textStyle="bold"/>
<Button
android:id="@+id/read_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/temp_and_press_textview"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:text="READ"
android:textSize="15sp"/>
RelativeLayout>
AndroidManifest.xml
<manifest package="com.lhw.bmp180"
xmlns:android="http://schemas.android.com/apk/res/android"
android:sharedUserId="android.uid.system">
<uses-sdk
android:minSdkVersion="19"
android:targetSdkVersion="25"
android:maxSdkVersion="25" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
intent-filter>
activity>
application>
manifest>
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_PACKAGE_NAME := bmp180
LOCAL_CERTIFICATE := platform
LOCAL_MODULE_TAGS := optional
LOCAL_DEX_PREOPT := false
LOCAL_PROGUARD_ENABLED:= disabled
LOCAL_SRC_FILES := $(call all-subdir-java-files)
include $(BUILD_PACKAGE)
默认编译的版本是 userdebug。其中配置了 SEAndroid 仅为 Permissive(permissive 级别:Linux 下 selinux 所设置的安全策略都会被启动,但是所有与 selinux 安全策略有关的服务或者程序不会被策略阻止,但是会收到警告。也就是所有操作都被允许(即没有 MAC),但是如果有违反权限的话,会记录日志),但是当切换到生产版本一定是 Enforcing 模式(enforcing 级别:Linux 下 selinux 所设置的安全策略都会被启用。所有与 selinux 安全策略有关的服务或者程序都会被策略阻止。也就是所有操作都会进行权限检查)。
NanoPC-T4 如何修改 SEAndroid 默认配置呢?直接修改 parameter.txt 中 CMDLINE 中的 androidboot.selinux 即可。比如下面的配置默认 SEAndroid 为 Enforcing 模式。
device/rockchip/rk3399/nanopc-t4/parameter.txt
CMDLINE: console=ttyFIQ0 androidboot.baseband=N/A androidboot.selinux=enforcing androidboot.veritymode=enforcing androidboot.hardware=rk30board androidboot.console=ttyFIQ0 init=/init initrd=0x62000000,0x00800000 mtdparts=rk29xxnand:0x00002000@0x00002000(uboot),0x00002000@0x00004000(trust),0x00002000@0x00006000(misc),0x00008000@0x00008000(resource),0x0000C000@0x00010000(kernel),0x00010000@0x0001C000(boot),0x00020000@0x0002C000(recovery),0x00038000@0x0004C000(backup),0x00002000@0x00084000(security),0x00100000@0x00086000(cache),0x00300000@0x00186000(system),0x00008000@0x00486000(metadata),0x00100000@0x0048E000(vendor),0x00080000@0x0058E000(oem),0x00002000@0x0060E000(frp),-@0x00610000(userdata)
下面是一部分关于 SEAndroid 报错日志,导致 Bmp180 service 无法在 SystemServer 中启动。
2020-08-15 09:47:48.900 9678-9678/? I/[email protected]: Bmp180 registerAsService
2020-08-15 09:47:48.901 9678-9678/? I/ServiceManagement: Removing namespace from process name [email protected] to [email protected].
2020-08-15 09:47:48.903 243-243/? E/SELinux: avc: denied { add } for interface=android.hardware.bmp180::IBmp180 sid=u:r:hal_bmp180_default:s0 pid=9678 scontext=u:r:hal_bmp180_default:s0 tcontext=u:object_r:default_android_hwservice:s0 tclass=hwservice_manager permissive=0
2020-08-15 09:47:48.903 9678-9678/? A//vendor/bin/hw/[email protected]: service.cpp:21] Check failed: status == android::OK (status=-2147483648, android::OK=0) Failed to register bmp180 HAL implementation
2020-08-15 09:47:48.904 9678-9678/? A/libc: Fatal signal 6 (SIGABRT), code -6 in tid 9678 ([email protected]), pid 9678 ([email protected])
2020-08-15 09:47:48.924 9682-9682/? A/DEBUG: pid: 9678, tid: 9678, name: [email protected] >>> /vendor/bin/hw/[email protected] <<<
2020-08-15 09:47:48.925 9682-9682/? A/DEBUG: Abort message: 'service.cpp:21] Check failed: status == android::OK (status=-2147483648, android::OK=0) Failed to register bmp180 HAL implementation'
2020-08-15 09:47:48.928 9682-9682/? A/DEBUG: #03 pc 0000000000002774 /vendor/bin/hw/[email protected] (main+616)
2020-08-15 09:47:48.928 9682-9682/? A/DEBUG: #05 pc 00000000000021d8 /vendor/bin/hw/[email protected] (_start_main+80)
2020-08-15 09:47:50.206 9603-9603/system_process I/SystemServer: bmp180 Service
2020-08-15 09:47:50.206 9603-9603/system_process I/zygote64: Looking for service [email protected]::IBmp180/default
2020-08-15 09:47:50.207 243-243/? E/SELinux: avc: denied { find } for interface=android.hardware.bmp180::IBmp180 sid=u:r:system_server:s0 pid=9603 scontext=u:r:system_server:s0 tcontext=u:object_r:default_android_hwservice:s0 tclass=hwservice_manager permissive=0
2020-08-15 09:47:50.207 9603-9603/system_process E/zygote64: service [email protected]::IBmp180 declares transport method EMPTY but framework expects hwbinder.
2020-08-15 09:47:50.208 9603-9603/system_process E/SystemServer: BOOT FAILURE starting Bmp180 service
java.util.NoSuchElementException
at android.os.HwBinder.getService(Native Method)
at android.hardware.bmp180.V1_0.IBmp180.getService(IBmp180.java:44)
at com.android.server.bmp180.Bmp180Service.(Bmp180Service.java:16)
at com.android.server.SystemServer.startOtherServices(SystemServer.java:1882)
at com.android.server.SystemServer.run(SystemServer.java:395)
at com.android.server.SystemServer.main(SystemServer.java:271)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:787)
由于机器没有完全适配 Enforcing 模式,会存在无法启动的问题,因此为了验证自己写的 SEAndroid 配置是否正确,可以将 androidboot.selinux 配置为 permissive。只需要把打印的 SEAndroid Log 都解决即可。
device/rockchip/common/sepolicy/device.te
......
#for bmp180
type bmp180_device, dev_type;
device/rockchip/common/sepolicy/file_contexts
......
#for bmp180
/dev/bmp180 u:object_r:bmp180_device:s0
hal_bmp180_default.te 文件是自己新建的,内容如下。现在重点关注:
allow hal_bmp180_default bmp180_device:chr_file {open read write ioctl};
这表示允许 hal_bmp180_default 访问 /dev/bmp180 节点,权限为 open read write ioctl。
system/sepolicy/vendor/hal_bmp180_default.te
type hal_bmp180_default, domain;
hal_server_domain(hal_bmp180_default, hal_bmp180)
type hal_bmp180_default_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(hal_bmp180_default)
allow hal_bmp180_default hal_bmp180_hwservice:hwservice_manager { find add };
allow hal_bmp180_default hidl_base_hwservice:hwservice_manager add;
allow hal_bmp180_default hal_bmp180_hwservice:binder call;
allow hal_bmp180_default bmp180_device:chr_file {open read write ioctl};
allow hal_bmp180_default hwservicemanager_prop:file r_file_perms;
allow hal_bmp180_default hwservicemanager:binder { transfer call };
system/sepolicy/vendor/file_contexts
......
/(vendor|system/vendor)/bin/hw/android\.hardware\.bmp180\@1\.0-service u:object_r:hal_bmp180_default_exec:s0
接下来新建文件 hal_bmp180_default.te,这在上一个步骤已经创建了。
system/sepolicy/public/attributes
......
attribute hal_bmp180;
expandattribute hal_bmp180 true;
attribute hal_bmp180_client;
expandattribute hal_bmp180_client true;
attribute hal_bmp180_server;
expandattribute hal_bmp180_server false;
system/sepolicy/public/hwservice.te
......
type hal_bmp180_hwservice, hwservice_manager_type;
创建文件 hal_bmp180.te。
system/sepolicy/public/hal_bmp180.te
# HwBinder IPC from client to server, and callbacks
binder_call(hal_bmp180_client, hal_bmp180_server)
binder_call(hal_bmp180_server, hal_bmp180_client)
add_hwservice(hal_bmp180_server, hal_bmp180_hwservice)
allow hal_bmp180_client hal_bmp180_hwservice:hwservice_manager find;
和 api 目录同步:
attributes、hwservice.te 和 hal_bmp180.te 修改同样追加到 system/sepolicy/prebuilts/api/26.0/public/ 相应文件中。不存在的文件直接拷贝过去。
system/sepolicy/private/hwservice_contexts
android.hardware.bmp180::IBmp180 u:object_r:hal_bmp180_hwservice:s0
system/sepolicy/private/compat/26.0/26.0.ignore.cil
(typeattributeset new_objects
( ......
hal_bmp180_hwservice
bmp180_service))
和 api 目录同步:
hwservice_contexts 同步修改到 system/sepolicy/prebuilts/api/26.0/private/ 下。
system/sepolicy/public/service.te
......
type bmp180_service, system_api_service, system_server_service, service_manager_type;
和 api 目录同步:
system/sepolicy/prebuilts/api/26.0/public/service.te 追加同样的内容。
此处 bmp180 要和 Context 类中定义的标志一样。
system/sepolicy/private/service_contexts
......
bmp180 u:object_r:bmp180_service:s0
和 api 目录同步:
system/sepolicy/prebuilts/api/26.0/private/service_contexts 追加同样的内容。
system/sepolicy/private/system_server.te
......
# Use HALs
......
hal_client_domain(system_server, hal_bmp180)
......
allow system_server hal_bmp180_hwservice:hwservice_manager find;
和 api 目录同步:
system/sepolicy/prebuilts/api/26.0/private/system_server.te 追加同样的内容。
到现在开发已经全部结束了,现在全编代码,烧写到单板上进行测试。
运行 Log 如下,这是 open 函数调用流程。从Log 也不难看出跨了三个进程,分别是 APP 进程、系统进程(system_process)和 HAL 进程。
2020-08-15 20:23:31.300 2811-2811/? D/Bmp180Manager: Bmp180Manager
2020-08-15 20:23:31.300 2811-2811/? D/Bmp180Manager: open
2020-08-15 20:23:31.301 528-2769/system_process D/Bmp180Service: open()
2020-08-15 20:23:31.304 267-267/? I/Bmp180Hal: Bmp180::open() fd=7