Android 8.1 【FriendlyARM】读取 BMP180 温度和压力系统服务、APP 开发

上一节完成了 BMP180 HAL 开发,现在再来继续后续内容:Bmp180Service 服务开发、APP 开发和处理 SEAndroid。

一、Bmp180Service 服务开发

先到 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 编译一下,检查代码是否存在问题。

二、App 开发

这里只贴出 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)

三、处理 SEAndroid

默认编译的版本是 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 都解决即可。

3.1 bmp180 新设备节点增加访问权限

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 };

3.2 增加 HAL service 访问权限

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/ 下。

3.3 增加 Framework 访问 HAL 权限

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

最后来看一下 APP 截图效果。不得不说武汉现在不开空调室内温度很高:34.4℃。
Android 8.1 【FriendlyARM】读取 BMP180 温度和压力系统服务、APP 开发_第1张图片

你可能感兴趣的:(NanoPC-T4)