This article uses a treadmill as a hypothetical scenario to introduce how to add HIDL on Android Q (10). The content involves HAL, HIDL inter-process communication, multi-threading and serial port operation, covering the main technologies of standard Android hardware services. When writing, I referred to two blogs (see reference materials) after debugging, expansion, and checking for gaps. Record this article to avoid pitfalls for latecomers, and the code has been verified on the RK3399_Android_10 code.
Table of contents
1.1 Write HAL -- interface definition
1.2 Use hidl-gen to generate variables
1.3 Realize the main program CPP
Three, SELinux part - hal service
4. Realization of framewors layer
4.1 Implementation of System Service
4.2.1 APP calls the main Activity
一、Hardware Interface
1.1 Write HAL -- interface definition
Create a new hardware/interfaces/ledtreadmill/1.0 directory under the source code directory, and then create a file
ILedTreadmill.hal (main interface)
package [email protected];
import ILedTreadmillCallback;
interface ILedTreadmill {
OpenInitSerialPort(string address, int32_t baudrate, int32_t flags);
StartRead();//开始读取
StopRead();//结束读取
RCtlUnlock(int32_t inTime) generates (bool ret);//解锁
RCtlLock() generates (bool ret);
RCtlSetWeight(int32_t inWeight) generates (bool ret);//设置体重 -- 计算卡路里用途 单位0.1千克
RCtlSetResumeType(bool inToMinSpeed) generates (bool ret);
RCtlStart() generates (int32_t ret);//开始
RCtlFastSpeed(int32_t inSpeed) generates (int32_t ret); // 快捷速度 1 - 12 千米每小时
RCtlFastIncline(int32_t inIncline) generates (int32_t ret); // 快捷速度 1 - 12 千米每小时
RCtlIncSpeed() generates (int32_t ret); //速度加
RCtlDecSpeed() generates (int32_t ret); //速度减
RCtlIncIncline() generates (int32_t ret); //坡度加
RCtlDecIncline() generates (int32_t ret); //坡度减
RCtlSetUnit(int32_t inUnit) generates (bool ret); //
RCtlPause() generates (bool ret); //暂停
RCtlResume() generates (bool ret); //继续
RCtlStop() generates (bool ret); //停止
setCallBack(ILedTreadmillCallback callback) generates (bool ret);//回调读取的数据
};
ILedTreadmillCallback.hal (callback interface)
package [email protected];
interface ILedTreadmillCallback {
oneway onKeyDown(int32_t inKeyCode);
oneway onKeyPress(int32_t inKeyCode);
oneway onKeyUp(int32_t inKeyCode);
oneway onFastSpeedKnobScroll(int32_t inKnobSpeed, int32_t inKnobType);//快速速度旋钮按键消息
oneway onStartCountdown(); //倒计时开始
oneway onCountdown(int32_t inCount); //倒计时
oneway onStartRun(); //开始跑步
oneway onStopRun(); //停止跑步
oneway onTargetSpeedChange(int inSpeed); //目标速度变化
oneway onTargetInclineChange(int inIncline); //目标扬升变化
oneway onTargetSpeedStatusChange(int inStatus); //目标速度 显示与隐藏 1 显示 0 隐藏
oneway onTargetInclineStatusChange(int inStatus); //目标扬升 显示与隐藏 1 显示 0 隐藏
};
1.2 Use hidl-gen to generate variables
First make sure the code is fully compiled, then execute make hidl-gen -j4
. /opt/openjdk8-env.sh
source build/envsetup.sh
lunch rk3399_Android10-userdebug
make -j8
make hidl-gen -j4
set temporary variable
[email protected]
LOC=hardware/interfaces/ledtreadmill/1.0/default
Use hidl-gen to generate C++ files in the default directory
hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE
Use hidl-gen to generate the Android.bp file in the default directory
hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE
Use update-makefiles.sh to generate Android.bp in the 1.0 directory
./hardware/interfaces/update-makefiles.sh
The directory structure at this time is
.
└── 1.0
├── Android.bp
├── default
│ ├── Android.bp
│ ├── LedTreadmillCallback.cpp
│ ├── LedTreadmillCallback.h
│ ├── LedTreadmill.cpp
│ └── LedTreadmill.h
├── ILedTreadmillCallback.hal
└── ILedTreadmill.hal
1.3 Realize the main program CPP
The LetTreadmill.h code is generated from the previous article, and the log definition is added manually
// FIXME: your file license if you have one
#pragma once
#include <android/hardware/ledtreadmill/1.0/ILedTreadmill.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
#include "android/log.h"
static const char *TAG="LedTreadmill";
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG , TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO , TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN , TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , TAG, __VA_ARGS__)
namespace android {
namespace hardware {
namespace ledtreadmill {
namespace V1_0 {
namespace implementation {
using ::android::hardware::hidl_array;
using ::android::hardware::hidl_memory;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::sp;
struct LedTreadmill : public ILedTreadmill {
// Methods from ::android::hardware::ledtreadmill::V1_0::ILedTreadmill follow.
Return<void> OpenInitSerialPort(const hidl_string& address, int32_t baudrate, int32_t flags) override;
Return<void> StartRead() override;
Return<void> StopRead() override;
Return<void> RCtlUnlock(int32_t inTime) override;
Return<void> RCtlLock() override;
Return<void> RCtlSetWeight(int32_t inWeight) override;
Return<void> RCtlSetResumeType(bool inToMinSpeed) override;
Return<void> RCtlStart() override;
Return<void> RCtlFastSpeed(int32_t inSpeed) override;
Return<void> RCtlFastIncline(int32_t inIncline) override;
Return<void> RCtlIncSpeed() override;
Return<void> RCtlDecSpeed() override;
Return<void> RCtlIncIncline() override;
Return<void> RCtlDecIncline() override;
Return<void> RCtlSetUnit(int32_t inUnit) override;
Return<void> RCtlPause() override;
Return<void> RCtlResume() override;
Return<void> RCtlStop() override;
Return<void> setCallBack(const sp<::android::hardware::ledtreadmill::V1_0::ILedTreadmillCallback>& callback) override;
// Methods from ::android::hidl::base::V1_0::IBase follow.
};
// FIXME: most likely delete, this is only for passthrough implementations
// extern "C" ILedTreadmill* HIDL_FETCH_ILedTreadmill(const char* name);
} // namespace implementation
} // namespace V1_0
} // namespace ledtreadmill
} // namespace hardware
} // namespace android
LetTreadmill.cpp
// FIXME: your file license if you have one
#include <bitset>
#include <cstring>
#include <fcntl.h>
#include <list>
#include <stdio.h>
#include <thread>
#include <termios.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <sstream>
#include <vector>
#include <mutex>
#include "LedTreadmill.h"
using namespace std;
typedef unsigned char UC;
namespace android {
namespace hardware {
namespace ledtreadmill {
namespace V1_0 {
namespace implementation {
#define OUTPUT_HEX_COLS 16
void output_Hex(const unsigned char *ucPtr, int iNumBytes);
int fd = -1;
sp<ILedTreadmillCallback> mCallback = nullptr;
pthread_t mReadThread;//读取数据线程
pthread_t mWriteThread;//写入数据线程
//获取波特率
static int getBaudrate(int baudrate) {
switch(baudrate) {
case 0: return B0;
case 50: return B50;
case 75: return B75;
case 110: return B110;
case 134: return B134;
case 150: return B150;
case 200: return B200;
case 300: return B300;
case 600: return B600;
case 1200: return B1200;
case 1800: return B1800;
case 2400: return B2400;
case 4800: return B4800;
case 9600: return B9600;
case 19200: return B19200;
case 38400: return B38400;
case 57600: return B57600;
case 115200: return B115200;
case 230400: return B230400;
case 460800: return B460800;
case 500000: return B500000;
case 576000: return B576000;
case 921600: return B921600;
case 1000000: return B1000000;
case 1152000: return B1152000;
case 1500000: return B1500000;
case 2000000: return B2000000;
case 2500000: return B2500000;
case 3000000: return B3000000;
case 3500000: return B3500000;
case 4000000: return B4000000;
default: return -1;
}
}
//读取串口数据
int UART_recv(void *buf, int len) {
//LOGD("UART_recv");
//定义读事件集合
fd_set fdRead;
int ret;
struct timeval aTime;
FD_ZERO(&fdRead);
FD_SET(fd,&fdRead);
aTime.tv_sec = 0;
aTime.tv_usec = 300000; //300ms
//ret = select(fd + 1, &fdRead, NULL, NULL, &aTime );
ret = select(fd + 1, &fdRead, NULL, NULL, NULL);
if (ret < 0) {
//关闭串口
close(fd);
} else if (ret > 0) {
//判断是否读事件
if (FD_ISSET(fd,&fdRead)) {
//data available, so get it!
ret = read(fd, buf, len);
// 对接收的数据进行处理,这里为简单的数据回发
}
}
return ret;
}
//读取一位数据
void recvOne(UC receiveBuffer[], int index) {
while(!UART_recv(&receiveBuffer[index], 1)) {
LOGD("recvOne ---1--- recv error");
}
}
void *runRead(void* run) {
long time = (long)run;
int iLCnt = 0;
LOGD("runRead %lu", time);
UC receiveBuffer[256];
int receiveLength;
while(1) {//读取一包数据
receiveLength = 0;
recvOne(receiveBuffer, receiveLength++);//读取第一位
if (receiveBuffer[receiveLength - 1] == 0x10) {
for(int i = 0; i < 15; i++) {
//LOGD("runRead --- read = %d", receiveBuffer[i]);
recvOne(receiveBuffer, receiveLength++);
}
output_Hex(receiveBuffer, 16);
if(iLCnt % 30 == 0){
LOGD("NMW runRead while: %d", iLCnt);
if(mCallback != nullptr){
if(iLCnt % 60 == 0){
mCallback->onKeyPress(iLCnt);
}else
mCallback->onTargetSpeedChange(iLCnt);
}
}
++iLCnt;
}
usleep(10 * 1000);
}
}
// Methods from ::android::hardware::ledtreadmill::V1_0::ILedTreadmill follow.
Return<void> LedTreadmill::OpenInitSerialPort(const hidl_string& address, int32_t baudrate, int32_t flags) {
// TODO implement
LOGD("OpenInitSerialPort");
int speed;
speed = getBaudrate(baudrate);
if (speed == -1) {
return Void();
}
char c[20];
strcpy(c, address.c_str());
if (flags == 0) {
fd = open(c, O_RDWR|O_NOCTTY);
}
if(fd < 0) {
LOGD("open uart device error\n");
}
struct termios cfg;
if (tcgetattr(fd, &cfg) < 0) {
close(fd);
return Void();
}
cfmakeraw(&cfg);
cfsetispeed(&cfg, speed);
cfsetospeed(&cfg, speed);
//修改控制模式,保证程序不会占用串口?
cfg.c_cflag |= CLOCAL;
//printf("c_cflag |= CLOCAL => %x\r\n", new_opt.c_cflag);
//修改控制模式,使得能够从串口读取输入数据
cfg.c_cflag |= CREAD;
//printf("c_cflag |= CREAD => %x\r\n", new_opt.c_cflag);
if (tcsetattr(fd, TCSANOW, &cfg) < 0) {
close(fd);
return Void();
}
return Void();
}
Return<void> LedTreadmill::StartRead() {
// TODO implement
int read = 1;
LOGD("StartRead");
pthread_create(&mReadThread, nullptr, runRead, &read);
return Void();
}
Return<void> LedTreadmill::StopRead() {
// TODO implement
LOGD("StopRead");
return Void();
}
Return<bool> LedTreadmill::RCtlUnlock(int32_t inTime) {
// TODO implement
LOGD("RCtlUnlock:%d", inTime);
return bool {};
}
Return<bool> LedTreadmill::RCtlLock() {
// TODO implement
LOGD("RCtlLock");
return bool {};
}
Return<bool> LedTreadmill::RCtlSetWeight(int32_t inWeight) {
// TODO implement
LOGD("RCtlSetWeight:%d", inWeight);
return bool {};
}
Return<bool> LedTreadmill::RCtlSetResumeType(bool inToMinSpeed) {
// TODO implement
if(inToMinSpeed)
LOGD("RCtlSetResumeType to MinSpeed");
else
LOGD("RCtlSetResumeType False");
return bool {};
}
Return<int32_t> LedTreadmill::RCtlStart() {
// TODO implement
LOGD("RCtlStart");
return int32_t {};
}
Return<int32_t> LedTreadmill::RCtlFastSpeed(int32_t inSpeed) {
// TODO implement
LOGD("RCtlFastSpeed:%d", inSpeed);
return int32_t {};
}
Return<int32_t> LedTreadmill::RCtlFastIncline(int32_t inIncline) {
// TODO implement
LOGD("RCtlFastIncline:%d", inIncline);
return int32_t {};
}
Return<int32_t> LedTreadmill::RCtlIncSpeed() {
// TODO implement
LOGD("RCtlIncSpeed");
return int32_t {};
}
Return<int32_t> LedTreadmill::RCtlDecSpeed() {
// TODO implement
LOGD("RCtlDecSpeed");
return int32_t {};
}
Return<int32_t> LedTreadmill::RCtlIncIncline() {
// TODO implement
LOGD("RCtlIncIncline");
return int32_t {};
}
Return<int32_t> LedTreadmill::RCtlDecIncline() {
// TODO implement
LOGD("RCtlDecIncline");
return int32_t {};
}
Return<bool> LedTreadmill::RCtlSetUnit(int32_t inUnit) {
// TODO implement
LOGD("RCtlSetUnit:%d", inUnit);
return bool {};
}
Return<bool> LedTreadmill::RCtlPause() {
// TODO implement
LOGD("RCtlPause");
return bool {};
}
Return<bool> LedTreadmill::RCtlResume() {
// TODO implement
LOGD("RCtlResume");
return bool {};
}
Return<bool> LedTreadmill::RCtlStop() {
// TODO implement
LOGD("RCtlStop");
return bool {};
}
Return<bool> LedTreadmill::setCallBack(const sp<::android::hardware::ledtreadmill::V1_0::ILedTreadmillCallback>& callback) {
// TODO implement
LOGD("setCallback");
mCallback = callback;
return bool {};
}
void output_Hex(const unsigned char *ucPtr, int iNumBytes)
{
int rowIdx, colIdx;
const unsigned char *iPtr = ucPtr;
char buf[128];
char *ptr = buf;
for (rowIdx = 0;
rowIdx < (iNumBytes + OUTPUT_HEX_COLS - 1) / OUTPUT_HEX_COLS; rowIdx++)
{
ptr = buf;
sprintf(ptr, "%08XH ", rowIdx * OUTPUT_HEX_COLS);
ptr += 10;
int offset = rowIdx * OUTPUT_HEX_COLS;
/* output hex characters */
for (colIdx = 0; colIdx < OUTPUT_HEX_COLS; colIdx++)
{
if ((offset + colIdx) >= iNumBytes) {
sprintf(ptr, "%s", " ");
ptr += 3;
}
else {
if ((colIdx + 1) == (OUTPUT_HEX_COLS / 2)) {
sprintf(ptr, "%02X ", (int)(iPtr[offset + colIdx]));
ptr += 5;
}
else {
sprintf(ptr, "%02X ", (int)(iPtr[offset + colIdx]));
ptr += 3;
}
}
} /* end-for (colIdx) */
sprintf(ptr, "%s", " ");
ptr += 3;
/* output ascii characters (if printable) */
for (colIdx = 0; colIdx < OUTPUT_HEX_COLS; colIdx++)
{
if ((offset + colIdx) >= iNumBytes)
sprintf(ptr, "%s", " ");
else if ((iPtr[offset + colIdx] >= 33) && (iPtr[offset + colIdx] <= 126))
sprintf(ptr, "%c", (char)(iPtr[offset + colIdx]));
else
sprintf(ptr, "%s", ".");
++ptr;
} /* end-for (colIdx) */
LOGI("%s", buf);
} /* end-for (rowIdx) */
return;
}
// Methods from ::android::hidl::base::V1_0::IBase follow.
//ILedTreadmill* HIDL_FETCH_ILedTreadmill(const char* /* name */) {
//return new LedTreadmill();
//}
//
} // namespace implementation
} // namespace V1_0
} // namespace ledtreadmill
} // namespace hardware
} // namespace android
Add startup script
New [email protected] script
service ledtreadmill-hal-1-0 /vendor/bin/hw/[email protected]
class hal
user system
group system
Create a new service.cpp to start and initialize
#define LOG_TAG "ledtreadmill-1.0-service"
#include <android/log.h>
#include <hidl/HidlTransportSupport.h>
#include "LedTreadmill.h"
using android::sp;
using android::status_t;
using android::OK;
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
using android::hardware::ledtreadmill::V1_0::ILedTreadmill;
using android::hardware::ledtreadmill::V1_0::implementation::LedTreadmill;
int main(int /* argc */, char* /* argv */ []) {
android::sp<ILedTreadmill> service = new LedTreadmill();
configureRpcThreadpool(4, true /*callerWillJoin*/);
status_t status = service->registerAsService();
if (status == OK) {
LOGD("LedTreadmill HAL Ready.");
service->initSerialPort("/dev/ttyS1", 9600, 0);
service->startRead();
joinRpcThreadpool();
}
LOGD("Cannot register LedTreadmill HAL service");
return 1;
}
Modify Android.bp pay attention to double check
// FIXME: your file license if you have one
cc_library_shared {
// FIXME: this should only be -impl for a passthrough hal.
// In most cases, to convert this to a binderized implementation, you should:
// - change '-impl' to '-service' here and make it a cc_binary instead of a
// cc_library_shared.
// - add a *.rc file for this module.
// - delete HIDL_FETCH_I* functions.
// - call configureRpcThreadpool and registerAsService on the instance.
// You may also want to append '-impl/-service' with a specific identifier like
// '-vendor' or '-<hardware identifier>' etc to distinguish it.
name: "[email protected]",
relative_install_path: "hw",
// FIXME: this should be 'vendor: true' for modules that will eventually be
// on AOSP.
proprietary: true,
srcs: [
"LedTreadmill.cpp",
"LedTreadmillCallback.cpp",
],
shared_libs: [
"libhidlbase",
"libhidltransport",
"libutils",
"[email protected]",
],
}
cc_binary {
name: "[email protected]",
relative_install_path: "hw",
defaults: ["hidl_defaults"],
proprietary: true,
init_rc: ["[email protected]"],
srcs: [
"LedTreadmill.cpp",
"service.cpp",
],
shared_libs: [
"libbase",
"liblog",
"libdl",
"libutils",
"libhardware",
"libhidlbase",
"libhidltransport",
"[email protected]",
],
}
Call update-makefiles.sh to update
Current directory structure:
.
└── 1.0
├── Android.bp
├── default
│ ├── Android.bp
│ ├── [email protected]
│ ├── LedTreadmillCallback.cpp
│ ├── LedTreadmillCallback.h
│ ├── LedTreadmill.cpp
│ ├── LedTreadmill.h
│ └── service.cpp
├── ILedTreadmillCallback.hal
└── ILedTreadmill.hal
Compile it separately and debug
mm ./hardware/interfaces/ledtreadmill/1.0
1.4 VNDK related
Under the directory build/target/product/gsi, current.txt and 29.txt are added in alphabetical order
VNDK-core: [email protected]
Two, the device part
We use RK3399 to do it, the lunch is rk3399_Android10-userdebug, modify the device.mk and manifest.xml in the device/rockchip/rk3399 directory, here is flexible according to your own environment. If the ab partition is configured manifest.xml may be manifest_ab.xml
device.mk
# ledtreadmill HAL
PRODUCT_PACKAGES += \
[email protected] \
[email protected]
manifest.xml
<hal format="hidl">
<name>android.hardware.ledtreadmill</name>
<transport>hwbinder</transport>
<version>1.0</version>
<interface>
<name>ILedTreadmill</name>
<instance>default</instance>
</interface>
</hal>
Three, SELinux part - hal service
3.1 vendor directory
file_contexts increase
/(vendor|system/vendor)/bin/hw/android\.hardware\.ledtreadmill@1\.0-service u:object_r:hal_ledtreadmill_default_exec:s0
New hal_ledtreadmill_default.te
type hal_ledtreadmill_default, domain;
hal_server_domain(hal_ledtreadmill_default, hal_ledtreadmill)
type hal_ledtreadmill_default_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(hal_ledtreadmill_default)
allow hal_ledtreadmill_default serial_device:chr_file rw_file_perms;
3.2 public directory
attributesadd
hal_attribute(ledtreadmill);
New hal_ledtreadmill.te
# HwBinder IPC from client to server, and callbacks
binder_call(hal_ledtreadmill_client, hal_ledtreadmill_server)
binder_call(hal_ledtreadmill_server, hal_ledtreadmill_client)
add_hwservice(hal_ledtreadmill_server, hal_ledtreadmill_hwservice)
allow hal_ledtreadmill_client hal_ledtreadmill_hwservice:hwservice_manager find;
Then sync to prebuilts/api/29.0/public
3.3 private directory
hwservice_contexts added
android.hardware.ledtreadmill::ILedTreadmill u:object_r:hal_ledtreadmill_hwservice:s0
private/compat/28.0/28.0.ignore.cil private/compat/27.0/27.0.ignore.cil
Private/compat/26.0/26.0.ignore.cil three files are added
hal_ledtreadmill_hwservice
Then sync to prebuilts/api/29.0/private
4. Realization of framewors layer
4.1 Implementation of System Service
4.1.1 Manager
Create a ledtreadmill directory under frameworks/base/core/java/android/os, and create a new ILedTreadmillService.aidl in this directory corresponding to the HAL layer function
// ILedTreadmillService.aidl
package android.os.ledtreadmill;
import android.os.ledtreadmill.ILedTreadmillListener;
// Declare any non-default types here with import statements
interface ILedTreadmillService {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
//解锁 -- 单位:分钟 超时后跑步机停止运行。
boolean RCtlUnlock(int inTime);
//关闭 -- 主动停止跑步机运行。
boolean RCtlLock();
//设置体重 -- 计算卡路里用途 单位0.1千克
boolean RCtlSetWeight(int inWeight);
//决定暂停后启动是否恢复到最小速度,(默认为true);
boolean RCtlSetResumeType(boolean inToMinSpeed);
//APK按键启动、停止、快捷速度。
int RCtlStart();
int RCtlFastSpeed(int inSpeed); // 快捷速度 1 - 12 千米每小时
int RCtlFastIncline(int inIncline); // 快捷速度 1 - 12 千米每小时
int RCtlIncSpeed(); //速度加
int RCtlDecSpeed(); //速度减
int RCtlIncIncline(); //坡度加
int RCtlDecIncline(); //坡度减
boolean RCtlSetUnit(int inUnit); //单位转换设置 0 为公制,1为英制。
boolean RCtlPause(); //暂停
boolean RCtlResume(); //继续
boolean RCtlStop(); //停止
boolean setLedTreadmillListener(ILedTreadmillListener listener);
}
Create ILedTreadmillListener.aidl, which corresponds to the callback interface
package android.os.ledtreadmill;
interface ILedTreadmillListener {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void onKeyDown(int inKeyCode);//键盘消息
void onKeyPress(int inKeyCode);//键盘消息
void onKeyUp(int inKeyCode);//键盘消息
void onFastSpeedKnobScroll(int inKnobSpeed, int inKnobType);//快速速度旋钮按键消息
void onStartCountdown(); //倒计时开始
void onCountdown(int inCount);//倒计时
void onStartRun(); //开始跑步
void onStopRun(); //停止跑步
void onTargetSpeedChange(int inSpeed); //目标速度变化
void onTargetInclineChange(int inIncline); //目标扬升变化
void onTargetSpeedStatusChange(int inStatus); //目标速度 显示与隐藏 1 显示 0 隐藏
void onTargetInclineStatusChange(int inStatus); //目标扬升 显示与隐藏 1 显示 0 隐藏
}
Create LedTreadmillListener.java
package android.os.ledtreadmill;
/**
* android.os.ledtreadmill
*/
public abstract class LedTreadmillListener extends ILedTreadmillListener.Stub {
}
Create LedTreadmillManager.java for APP to call
package android.os.ledtreadmill;
import android.os.RemoteException;
import android.os.ledtreadmill.LedTreadmillListener;
import android.util.Log;
import android.os.ledtreadmill.ILedTreadmillService;
public class LedTreadmillManager {
public static final String TAG = "LedTreadmillManager";
private ILedTreadmillService mService;
public LedTreadmillManager(ILedTreadmillService service) {
mService = service;
}
public boolean RCtlUnlock(int inTime) {
boolean bRet = false;
try {
Log.d(TAG, "RCtlUnlock: ");
if(mService != null){
bRet = mService.RCtlUnlock(inTime);
}
} catch (RemoteException e) {
e.printStackTrace();
}
return bRet;
}
public boolean RCtlLock() {
boolean bRet = false;
try {
if(mService != null){
bRet = mService.RCtlLock();
}
} catch (RemoteException e) {
e.printStackTrace();
}
return bRet;
}
public boolean RCtlSetWeight(int inWeight) {
boolean bRet = false;
try {
Log.d(TAG, "IRCtlSetWeight: " + inWeight);
if(mService != null){
bRet = mService.RCtlSetWeight(inWeight);
}
} catch (RemoteException e) {
e.printStackTrace();
}
return bRet;
}
public boolean RCtlSetResumeType(boolean inToMinSpeed) {
boolean bRet = false;
try {
Log.d(TAG, "RCtlSetResumeType: ");
if(mService != null){
bRet = mService.RCtlSetResumeType(inToMinSpeed);
}
} catch (RemoteException e) {
e.printStackTrace();
}
return bRet;
}
public int RCtlStart() {
int iRet = -16;
try {
Log.d(TAG, "RCtlStart: ");
if(mService != null){
iRet = mService.RCtlStart();
}
} catch (RemoteException e) {
e.printStackTrace();
}
return iRet;
}
public int RCtlFastSpeed(int inSpeed) {
int iRet = -16;
try {
Log.d(TAG, "RCtlFastSpeed: ");
if(mService != null)
iRet = mService.RCtlFastSpeed(inSpeed);
} catch (RemoteException e) {
e.printStackTrace();
}
return iRet;
}
public int RCtlFastIncline(int inIncline) {
int iRet = -16;
try {
Log.d(TAG, "RCtlFastIncline: ");
if(mService != null)
iRet = mService.RCtlFastIncline(inIncline);
} catch (RemoteException e) {
e.printStackTrace();
}
return iRet;
}
public int RCtlIncSpeed() {
int iRet = -16;
try {
Log.d(TAG, "RCtlIncSpeed: ");
if(mService != null)
iRet = mService.RCtlIncSpeed();
} catch (RemoteException e) {
e.printStackTrace();
}
return iRet;
}
public int RCtlDecSpeed() {
int iRet = -16;
try {
Log.d(TAG, "RCtlDecSpeed: ");
if(mService != null)
iRet = mService.RCtlDecSpeed();
} catch (RemoteException e) {
e.printStackTrace();
}
return iRet;
}
public int RCtlIncIncline() {
int iRet = -16;
try {
Log.d(TAG, "RCtlIncIncline: ");
if(mService != null)
iRet = mService.RCtlIncIncline();
} catch (RemoteException e) {
e.printStackTrace();
}
return iRet;
}
public int RCtlDecIncline() {
int iRet = -16;
try {
Log.d(TAG, "RCtlDecIncline: ");
if(mService != null)
iRet = mService.RCtlDecIncline();
} catch (RemoteException e) {
e.printStackTrace();
}
return iRet;
}
public boolean RCtlSetUnit(int inUnit) {
boolean bRet = false;
try {
Log.d(TAG, "RCtlSetUnit: ");
if(mService != null)
bRet = mService.RCtlSetUnit(inUnit);
RCtlSetUnit(inUnit);
} catch (RemoteException e) {
e.printStackTrace();
}
return bRet;
}
public boolean RCtlPause() {
boolean bRet = false;
try {
Log.d(TAG, "IRCtlPause: ");
if(mService != null)
bRet = mService.RCtlPause();
} catch (RemoteException e) {
e.printStackTrace();
}
return bRet;
}
public boolean RCtlResume() {
boolean bRet = false;
try {
Log.d(TAG, "IRCtlResume: ");
if(mService != null)
bRet = mService.RCtlResume();
} catch (RemoteException e) {
e.printStackTrace();
}
return bRet;
}
public boolean RCtlStop() {
boolean bRet = false;
try {
Log.d(TAG, "IRCtlStop: ");
//AIDL传消息过来时的处理必须放异常处理,否则本地APK异常时抛出异常会导致远程AIDL调用发生错误、崩溃。
//代码写这里
if(mService != null)
bRet = mService.RCtlStop();
} catch (RemoteException e) {
e.printStackTrace();
}
return bRet;
}
public boolean setLedTreadmillListener(ILedTreadmillListener listener){
Log.d(TAG, "setLedTreadmillListener: ");
try {
if (mService == null) {
return false;
}
return mService.setLedTreadmillListener(listener);
} catch (RemoteException e) {
e.printStackTrace();
return false;
}
}
}
Add in frameworks\base\Android.bp
"core/java/android/os/ledtreadmill/ILedTreadmillListener.aidl",
"core/java/android/os/ledtreadmill/ILedTreadmillService.aidl",
4.1.2 Service side
frameworks/base/services/core/Android.bp add reference
static_libs: [
......
"android.hardware.ledtreadmill-V1.0-java",
......
],
Create ledtreadmill directory under frameworks\base\services\core\java\com\android\server, and create a new file LedTreadmillService.java
package com.android.server.ledtreadmill;
import android.hardware.ledtreadmill.V1_0.ILedTreadmill;
import android.hardware.ledtreadmill.V1_0.ILedTreadmillCallback;
import android.os.RemoteException;
import android.util.Log;
import android.os.ledtreadmill.ILedTreadmillListener;
import android.os.ledtreadmill.ILedTreadmillService;
/**
* com.android.server.ledtreadmill.LedTreadmillService
*
* @author GW00175635
* @date 2019/7/11
*/
public class LedTreadmillService extends ILedTreadmillService.Stub {
private String TAG = "LedTreadmillService";
private ILedTreadmill mHalLedService ;
public LedTreadmillService(){
try {
mHalLedService = ILedTreadmill.getService();//获取service
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public boolean RCtlUnlock(int inTime) throws RemoteException {
Log.d(TAG, "RCtlUnlock: ");
mHalLedService.RCtlUnlock(inTime);
return true;
}
@Override
public boolean RCtlLock() throws RemoteException {
Log.d(TAG, "RCtlLock: ");
mHalLedService.RCtlLock();
return true;
}
@Override
public boolean RCtlSetWeight(int inWeight) throws RemoteException {
Log.d(TAG, "RCtlSetWeight: ");
mHalLedService.RCtlSetWeight(inWeight);
return true;
}
@Override
public boolean RCtlSetResumeType(boolean inToMinSpeed) throws RemoteException {
Log.d(TAG, "RCtlSetResumeType: ");
mHalLedService.RCtlSetResumeType(inToMinSpeed);
return true;
}
@Override
public int RCtlStart() throws RemoteException {
Log.d(TAG, "RCtlStart: ");
mHalLedService.RCtlStart();
return 0;
}
@Override
public int RCtlFastSpeed(int inSpeed) throws RemoteException {
Log.d(TAG, "RCtlFastSpeed: ");
mHalLedService.RCtlFastSpeed(inSpeed);
return 0;
}
@Override
public int RCtlFastIncline(int inIncline) throws RemoteException {
Log.d(TAG, "RCtlFastIncline: ");
mHalLedService.RCtlFastIncline(inIncline);
return 0;
}
@Override
public int RCtlIncSpeed() throws RemoteException {
Log.d(TAG, "RCtlIncSpeed: ");
mHalLedService.RCtlIncSpeed();
return 0;
}
@Override
public int RCtlDecSpeed() throws RemoteException {
Log.d(TAG, "RCtlDecSpeed: ");
mHalLedService.RCtlDecSpeed();
return 0;
}
@Override
public int RCtlIncIncline() throws RemoteException {
Log.d(TAG, "RCtlIncIncline: ");
mHalLedService.RCtlIncIncline();
return 0;
}
@Override
public int RCtlDecIncline() throws RemoteException {
Log.d(TAG, "RCtlDecIncline: ");
mHalLedService.RCtlDecIncline();
return 0;
}
@Override
public boolean RCtlSetUnit(int inUnit) throws RemoteException {
Log.d(TAG, "RCtlSetUnit: ");
mHalLedService.RCtlSetUnit(inUnit);
return true;
}
@Override
public boolean RCtlPause() throws RemoteException {
Log.d(TAG, "RCtlPause: ");
mHalLedService.RCtlPause();
return true;
}
@Override
public boolean RCtlResume() throws RemoteException {
Log.d(TAG, "RCtlResume: ");
mHalLedService.RCtlResume();
return true;
}
@Override
public boolean RCtlStop() throws RemoteException {
Log.d(TAG, "RCtlStop: ");
mHalLedService.RCtlStop();
return true;
}
@Override
public boolean setLedTreadmillListener(ILedTreadmillListener listener) throws RemoteException {
Log.d(TAG, "setTestEventListener: ");
LedTreadmillCallback ltCallback = new LedTreadmillCallback(listener);
//return mHalLedService.setCallback(ltCallback);
return false;
}
class LedTreadmillCallback extends ILedTreadmillCallback.Stub{
ILedTreadmillListener mILTListener;
LedTreadmillCallback (ILedTreadmillListener listener){
mILTListener = listener;
}
@Override
public void onKeyDown(int inKeyCode) throws RemoteException {
Log.d(TAG, "onKeyDown: ");
mILTListener.onKeyDown(inKeyCode);
}
@Override
public void onKeyPress(int inKeyCode) throws RemoteException {
Log.d(TAG, "onKeyPress: ");
mILTListener.onKeyPress(inKeyCode);
}
@Override
public void onKeyUp(int inKeyCode) throws RemoteException {
Log.d(TAG, "onKeyUp: ");
mILTListener.onKeyUp(inKeyCode);
}
@Override
public void onFastSpeedKnobScroll(int inKnobSpeed, int inKnobType) throws RemoteException {
Log.d(TAG, "onFastSpeedKnobScroll: ");
mILTListener.onFastSpeedKnobScroll(inKnobSpeed, inKnobType);
}
@Override
public void onStartCountdown() throws RemoteException {
Log.d(TAG, "onStartCountdown: ");
mILTListener.onStartCountdown();
}
@Override
public void onCountdown(int inCount) throws RemoteException {
Log.d(TAG, "onCountdown: ");
mILTListener.onCountdown(inCount);
}
@Override
public void onStartRun() throws RemoteException {
Log.d(TAG, "onStartRun: ");
mILTListener.onStartRun();
}
@Override
public void onStopRun() throws RemoteException {
Log.d(TAG, "onStopRun: ");
mILTListener.onStopRun();
}
}
}
Add in frameworks\base\core\java\android\content\Context.java
/**
* {@link android.os.ledtreadmill.LedTreadmillManager} for receiving intents at a
* time of your choosing.
*
* @see #getSystemService(String)
* @see android.os.ledtreadmill.LedTreadmillManager
*/
public static final String LEDTREADMILL_SERVICE = "led_treadmill";
frameworks/base/core/java/android/app/SystemServiceRegistry.java里添加
import android.os.ledtreadmill.LedTreadmillManager;
import android.os.ledtreadmill.ILedTreadmillService;
......
registerService(Context.LEDTREADMILL_SERVICE, LedTreadmillManager.class,
new CachedServiceFetcher<LedTreadmillManager>() {
@Override
public LedTreadmillManager createService(ContextImpl ctx) {
IBinder iBinder = ServiceManager.getService(Context.LEDTREADMILL_SERVICE);
if (iBinder == null) {
Log.d(TAG, "NMW get LedTreadmillManager return null");
return null;
}
ILedTreadmillService service = ILedTreadmillService.Stub
.asInterface(iBinder);
Log.d(TAG, "NMW get LedTreadmillManager return instance");
return new LedTreadmillManager(service);
}});
......
Add in frameworks\base\services/java/com/android/server/SystemServer.java
import com.android.server.ledtreadmill.LedTreadmillService;
......
/**
* Starts a miscellaneous grab bag of stuff that has yet to be refactored and organized.
*/
private void startOtherServices() {
......
traceBeginAndSlog("MakeLedTreadmillServiceReady");
try {
Slog.i(TAG, "LedTreadmill Service");
ServiceManager.addService(Context.LEDTREADMILL_SERVICE, new LedTreadmillService());
} catch (Throwable e) {
reportWtf("starting LedTreadmillService", e);
}
traceEnd();
......
4.1.3 Add Selinux policy
public/service.te add
type ledtreadmill_service, system_api_service, system_server_service, service_manager_type;
Add private/service_contexts--note that the led_treadmill here should be consistent with the Context.LEDTREADMILL_SERVICE added above
led_treadmill u:object_r:ledtreadmill_service:s0
add private/system_server.te
hal_client_domain(system_server, hal_ledtreadmill)
private/compat/28.0/28.0.ignore.cil private/compat/27.0/27.0.ignore.cil 以及
add private/compat/26.0/26.0.ignore.cil
ledtreadmill_service
Finally, synchronize these changes to prebuilts/api/29.0/ and you're done
4.1.4 Compile
The API needs to be updated before it can be compiled and passed
#./hardware/interfaces/update-makefiles.sh
make update-api
make -j8
4.2 APP implementation call
Write an APP to test and verify, create a new LedTreadmill directory under the packages/apps directory, the content is relatively simple - we will put the main files up, and fill in the missing ones.
4.2.1 APP calls the main Activity
package com.genlt.ledtreadmill;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ledtreadmill.LedTreadmillManager;
import android.os.ledtreadmill.LedTreadmillListener;
import android.view.View;
import android.widget.Button;
import android.util.Log;
/**
* MainaActivity
*/
public class MainActivity extends Activity implements View.OnClickListener {
private final String TAG = "MainActivity";
LedTreadmillManager mLedTreadmillManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLedTreadmillManager = (LedTreadmillManager)getSystemService(Context.LEDTREADMILL_SERVICE);
if(mLedTreadmillManager == null){
Log.d("activity", "Get service for mLedTreadmillManager failed." + Context.LEDTREADMILL_SERVICE);
}else
{
Log.d("activity", "Get service for mLedTreadmillManager OK. " + Context.LEDTREADMILL_SERVICE);
}
Button btn_start, btn_stop, btn_custprogram, btn_pause;
Button btn_SpeedPlus, btn_SpeedDec;
Button btn_InclinePlus, btn_InclineDec;
Button btn_FastSpeedTwo, btn_FastSpeedSix;
Button btn_SetCallback;
btn_start = findViewById(R.id.btn_start);
btn_stop = findViewById(R.id.btn_stop);
btn_pause = findViewById(R.id.btn_pause);
btn_custprogram = findViewById(R.id.btn_custprogram);
btn_SpeedPlus = findViewById(R.id.btn_SpeedPlus);
btn_SpeedDec = findViewById(R.id.btn_SpeedDec);
btn_InclinePlus = findViewById(R.id.btn_InclinePlus);
btn_InclineDec = findViewById(R.id.btn_InclineDec);
btn_FastSpeedTwo = findViewById(R.id.btn_FastSpeedTwo);
btn_FastSpeedSix = findViewById(R.id.btn_FastSpeedSix);
btn_SetCallback = findViewById(R.id.button_setCallback);
btn_start.setOnClickListener(this);
btn_stop.setOnClickListener(this);
btn_pause.setOnClickListener(this);
btn_custprogram.setOnClickListener(this);
btn_SpeedPlus.setOnClickListener(this);
btn_SpeedDec.setOnClickListener(this);
btn_FastSpeedTwo.setOnClickListener(this);
btn_FastSpeedSix.setOnClickListener(this);
btn_InclinePlus.setOnClickListener(this);
btn_InclineDec.setOnClickListener(this);
btn_SetCallback.setOnClickListener(this);
}
@Override
public void onClick(View view) {
Log.d(TAG, "onClick");
// TODO Auto-generated method stub
switch (view.getId()) {
case R.id.btn_start:
if(mLedTreadmillManager != null){
Log.d(TAG, "btn_start: ");
mLedTreadmillManager.RCtlStart();
}else{
Log.d(TAG, "mLedTreadmillManager is null: ");
}
break;
case R.id.btn_stop:
if(mLedTreadmillManager != null){
Log.d(TAG, "btn_stop: ");
mLedTreadmillManager.RCtlStart();
}else{
Log.d(TAG, "mLedTreadmillManager is null: ");
}
break;
case R.id.btn_SpeedPlus:
if(mLedTreadmillManager != null){
Log.d(TAG, "btn_SpeedPlus: ");
mLedTreadmillManager.RCtlStart();
}else{
Log.d(TAG, "mLedTreadmillManager is null: ");
}
break;
case R.id.btn_SpeedDec:
if(mLedTreadmillManager != null){
Log.d(TAG, "btn_SpeedDec: ");
mLedTreadmillManager.RCtlStart();
}else{
Log.d(TAG, "mLedTreadmillManager is null: ");
}
break;
case R.id.btn_FastSpeedTwo:
if(mLedTreadmillManager != null){
Log.d(TAG, "btn_FastSpeedTwo: ");
mLedTreadmillManager.RCtlFastSpeed(20);
}else{
Log.d(TAG, "mLedTreadmillManager is null: ");
}
break;
case R.id.btn_FastSpeedSix:
if(mLedTreadmillManager != null){
Log.d(TAG, "btn_FastSpeedSix: ");
mLedTreadmillManager.RCtlFastSpeed(60);
}else{
Log.d(TAG, "mLedTreadmillManager is null: ");
}
break;
case R.id.btn_custprogram:
if(mLedTreadmillManager != null){
// Log.d(TAG, "btn_custprogram: ");
// int []Speeds = {12, 15, 8};
// int []Incs = {2, 1, 9};
// int []Durations = {50, 50, 50};
// mLedTreadmillManager.RCtlCustProgram(Speeds, Incs, Durations);
}else{
Log.d(TAG, "mLedTreadmillManager is null: ");
}
break;
case R.id.btn_pause:
if(mLedTreadmillManager != null){
Log.d(TAG, "btn_pause: ");
mLedTreadmillManager.RCtlPause();
}else{
Log.d(TAG, "mLedTreadmillManager is null: ");
}
break;
case R.id.btn_InclinePlus:
if(mLedTreadmillManager != null){
Log.d(TAG, "btn_InclinePlus: ");
mLedTreadmillManager.RCtlIncIncline();
}else{
Log.d(TAG, "mLedTreadmillManager is null: ");
}
break;
case R.id.btn_InclineDec:
if(mLedTreadmillManager != null){
Log.d(TAG, "btn_InclineDec: ");
mLedTreadmillManager.RCtlDecIncline();
}else{
Log.d(TAG, "mLedTreadmillManager is null: ");
}
break;
case R.id.button_setCallback:
Log.d(TAG, "button_setCallback");
mLedTreadmillManager.setLedTreadmillListener(new LedTreadmillListener(){
@Override
public void onKeyDown(int inKeyCode) {
Log.d(TAG, "onKeyDown: " + inKeyCode);
}
@Override
public void onKeyPress(int inKeyCode) {
Log.d(TAG, "onKeyPress: " + inKeyCode);
}
@Override
public void onKeyUp(int inKeyCode) {
Log.d(TAG, "onKeyUp: " + inKeyCode);
}
@Override
public void onFastSpeedKnobScroll(int inKnobSpeed, int inKnobType) {
Log.d(TAG, "onFastSpeedKnobScroll inKnobSpeed: " + inKnobSpeed + " inKnobType:" + inKnobType);
}
@Override
public void onStartCountdown() {
Log.d(TAG, "onStartCountdown: ");
}
@Override
public void onCountdown(int inCount) {
Log.d(TAG, "onCountdown: " + inCount);
}
@Override
public void onStartRun() {
Log.d(TAG, "onStartRun: ");
}
@Override
public void onStopRun() {
Log.d(TAG, "onStopRun: ");
}
@Override
public void onTargetSpeedChange(int inSpeed) {
Log.d(TAG, "onTargetSpeedChange: " + inSpeed);
}
@Override
public void onTargetInclineChange(int inIncline) {
Log.d(TAG, "onTargetInclineChange: " + inIncline);
}
@Override
public void onTargetSpeedStatusChange(int inStatus) {
Log.d(TAG, "onTargetSpeedStatusChange: " + inStatus);
}
@Override
public void onTargetInclineStatusChange(int inStatus) {
Log.d(TAG, "onTargetInclineStatusChange: " + inStatus);
}
});
break;
default:
break;
}
}
}
4.2.2 Layout file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:orientation="horizontal">
<Button
android:id="@+id/btn_start"
android:layout_width="360dp"
android:layout_height="60dp"
android:gravity="center"
android:textSize="30sp"
android:text="启动 Start" />
<Button
android:id="@+id/btn_stop"
android:layout_width="360dp"
android:layout_height="60dp"
android:gravity="center"
android:textSize="30sp"
android:text="停止 Stop" />
</LinearLayout>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:orientation="horizontal">
<Button
android:id="@+id/btn_SpeedPlus"
android:layout_width="360dp"
android:layout_height="60dp"
android:gravity="center"
android:textSize="30sp"
android:text="加速 Plus" />
<Button
android:id="@+id/btn_SpeedDec"
android:layout_width="360dp"
android:layout_height="60dp"
android:gravity="center"
android:textSize="30sp"
android:text="减速 Dec" />
</LinearLayout>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:orientation="horizontal">
<Button
android:id="@+id/btn_FastSpeedTwo"
android:layout_width="360dp"
android:layout_height="60dp"
android:gravity="center"
android:textSize="30sp"
android:text="速度(Speed) 2 " />
<Button
android:id="@+id/btn_FastSpeedSix"
android:layout_width="360dp"
android:layout_height="60dp"
android:gravity="center"
android:textSize="30sp"
android:text="速度(Speed) 6 " />
</LinearLayout>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:orientation="horizontal">
<Button
android:id="@+id/btn_custprogram"
android:layout_width="360dp"
android:layout_height="60dp"
android:gravity="center"
android:textSize="30sp"
android:visibility="invisible"
android:text="程式 CustProgram" />
<Button
android:id="@+id/btn_pause"
android:layout_width="360dp"
android:layout_height="60dp"
android:gravity="center"
android:textSize="30sp"
android:text="暂停 Pause"
/>
</LinearLayout>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:orientation="horizontal">
<Button
android:id="@+id/btn_InclinePlus"
android:layout_width="360dp"
android:layout_height="60dp"
android:gravity="center"
android:textSize="30sp"
android:text="扬升加 Incline Plus" />
<Button
android:id="@+id/btn_InclineDec"
android:layout_width="360dp"
android:layout_height="60dp"
android:gravity="center"
android:textSize="30sp"
android:text="扬升减 Incline Dec" />
</LinearLayout>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:orientation="horizontal">
<Button
android:id="@+id/button_setCallback"
android:layout_width="360dp"
android:layout_height="60dp"
android:gravity="center"
android:textSize="30sp"
android:text="回调 SetCallBack" />
<Button
android:layout_width="360dp"
android:layout_height="60dp"
android:gravity="center"
android:textSize="30sp"
android:visibility="invisible"
android:text="" />
</LinearLayout>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="36sp"
android:text="跑步机控制示例Demo" />
</RelativeLayout>
</LinearLayout>
4.2.2 Makefile Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_CERTIFICATE := platform
LOCAL_MODULE_TAGS := optional
LOCAL_PACKAGE_NAME := LedTreadmillApp
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PRIVATE_PLATFORM_APIS := true
include $(BUILD_PACKAGE)
At this point, a complete DEMO is completed. Of course, this is just a demonstration, and there is no specific business logic in it. Readers can write and expand according to their own project needs on this basis.
References
Android 9 HAL development adds HIDL implementation process
Android HAL layer adds HIDL instance to realize serial port communication