当前位置: 代码迷 >> 综合 >> Android 8.1 【FriendlyARM】读取 BMP180 温度和压力系统服务、APP 开发
  详细解决方案

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

热度:14   发布时间:2024-02-11 13:24:42.0

上一节完成了 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>() {@Overridepublic BMP180TemperatureAndPressure createFromParcel(Parcel in) {return new BMP180TemperatureAndPressure(in);}@Overridepublic 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();}@Overridepublic 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;}@Overridepublic 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 bmp180registerService(Context.BMP180_SERVICE, Bmp180Manager.class, new CachedServiceFetcher<Bmp180Manager>() {@Overridepublic 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;@Overrideprotected 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() {@Overridepublic void onClick(View v) {BMP180TemperatureAndPressure bMP180TemperatureAndPressure = new BMP180TemperatureAndPressure(0L, 0L);mBmp180Manager.getTemperatureAndPressure(bMP180TemperatureAndPressure);mDisplayTextView.setText("Temp:" +bMP180TemperatureAndPressure.getTemperature() / 10.0f +"℃ Pressure:" +bMP180TemperatureAndPressure.getPressure() + "Pa");}});}@Overrideprotected void onResume() {super.onResume();mBmp180Manager = (Bmp180Manager) getSystemService(Context.BMP180_SERVICE);isOpen = mBmp180Manager.open();Log.d("lhw", "isOpen=" + isOpen);}@Overrideprotected void onPause() {super.onPause();if (isOpen) mBmp180Manager.close();}
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayoutxmlns: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"><TextViewandroid: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"/><Buttonandroid: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

<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lhw.bmp180"xmlns:android="http://schemas.android.com/apk/res/android"android:sharedUserId="android.uid.system"><uses-sdkandroid:minSdkVersion="19"android:targetSdkVersion="25"android:maxSdkVersion="25" /><applicationandroid: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/android.hardware.bmp180@1.0-service: Bmp180 registerAsService
2020-08-15 09:47:48.901 9678-9678/? I/ServiceManagement: Removing namespace from process name android.hardware.bmp180@1.0-service to bmp180@1.0-serv.
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/android.hardware.bmp180@1.0-service: 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 (bmp180@1.0-serv), pid 9678 (bmp180@1.0-serv)
2020-08-15 09:47:48.924 9682-9682/? A/DEBUG: pid: 9678, tid: 9678, name: bmp180@1.0-serv  >>> /vendor/bin/hw/android.hardware.bmp180@1.0-service <<<
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/android.hardware.bmp180@1.0-service (main+616)
2020-08-15 09:47:48.928 9682-9682/? A/DEBUG:     #05 pc 00000000000021d8  /vendor/bin/hw/android.hardware.bmp180@1.0-service (_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 android.hardware.bmp180@1.0::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 android.hardware.bmp180@1.0::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 servicejava.util.NoSuchElementExceptionat android.os.HwBinder.getService(Native Method)at android.hardware.bmp180.V1_0.IBmp180.getService(IBmp180.java:44)at com.android.server.bmp180.Bmp180Service.<init>(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_hwservicebmp180_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℃。
在这里插入图片描述

  相关解决方案