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
<?xml version="1.0" encoding="utf-8"?><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
<?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-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 := bmp180LOCAL_CERTIFICATE := platformLOCAL_MODULE_TAGS := optionalLOCAL_DEX_PREOPT := falseLOCAL_PROGUARD_ENABLED:= disabledLOCAL_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:[email protected](uboot),[email protected](trust),[email protected](misc),[email protected](resource),[email protected](kernel),[email protected](boot),[email protected](recovery),[email protected](backup),[email protected](security),[email protected](cache),[email protected](system),[email protected](metadata),[email protected](vendor),[email protected](oem),[email protected](frp),[email protected](userdata)
下面是一部分关于 SEAndroid 报错日志,导致 Bmp180 service 无法在 SystemServer 中启动。
2020-08-15 09:47:48.900 9678-9678/? I/[email protected]: Bmp180 registerAsService2020-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=02020-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 implementation2020-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 Service2020-08-15 09:47:50.206 9603-9603/system_process I/zygote64: Looking for service [email protected]::IBmp180/default2020-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=02020-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 bmp180type 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 callbacksbinder_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: Bmp180Manager2020-08-15 20:23:31.300 2811-2811/? D/Bmp180Manager: open2020-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的相关准备工作和学习的流程
- 最新Android(安卓)4.x 搭建开发环境
- Android(安卓)app开发中获取cpu arm架构信息及执行shell命令方法
- Android(安卓)快速开发之快速实现“我”界面
- android Process.killProcess 和 System.exit(0) 区别
- android开发者选项
- Android中Context的传递
- Android(安卓)Studio 之 对话框开发(Dialog) ---- 两种方式来实
- 一网打尽__Android(安卓)开源代码合集(WebView框架)