本博文配套的硬件是瑞芯微RK3288芯片,跑的是Android5.1的SDK。作为设备开发商,经常有release接口给第三方调用的需求,本文基于实际案例撰写,不同版本的Android Framework架构可能略有不同,但是大同小异,均可作为参考。
第三方APP如果要操作硬件,比如通过ioctl操作某个GPIO,大体上来说,应该有以下两种方法:
1、我们封装操作硬件的API,形成一个so库给到第三方,并写好JNI接口打通到Java层,第三方直接调用Java层接口就好。这种方法理论上可行,但是实际操作中一般会有API调用权限问题。操作硬件接口,一般都需要调用者进程需要system权限甚至是root权限,通过release JNI接口的方式给第三方调用,所有的代码都是跑在第三方APP的进程,一般而言,我们是不会随便给第三方APP系统权限,这样既会有安全问题,也会引入第三方APP升级麻烦等问题,不推荐使用。
2、扩展Framework接口:第三方APP调用标准接口,然后通过aidl的方式,跨进程调用到system_server进程中对应的远程接口,由于system_server是系统进程,所以不存在权限问题。本文就是使用该种方式。
在 frameworks/base/core/java目录下,根据包名创建一个子目录,比如我创建的是com/wrtsz/api/,在该目录下,新建一个java文件,比如WrtdevManager.java ,对应的代码如下:
package com.wrtsz.api;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
public class WrtdevManager {
private final IWrtdevManager mService;
private Handler mHandler;
private static final String TAG = "WrtdevManager";
public WrtdevManager(IWrtdevManager service, Handler handler)
{
mHandler = handler;
mService = service;
}
public int getMicroWaveState()
{
try{
return mService.getMicroWaveState();
}catch (RemoteException e){
Log.e(TAG, "getMicroWaveState failed");
return -1;
}
}
public byte[] getIcCardNo()
{
try{
return mService.getIcCardNo();
}catch (RemoteException e){
Log.e(TAG, "getIcCardNo failed");
return null;
}
}
public int openLed(int opIndex)
{
try{
return mService.openLed(opIndex);
}catch (RemoteException e){
Log.e(TAG, "openWhiteLed failed");
return -1;
}
}
public int openDoor()
{
try{
return mService.openDoor();
}catch (RemoteException e){
Log.e(TAG, "openDoor failed");
return -1;
}
}
}
以上代码,注意两点:
1、每个接口中,都必须catch RemoteException异常,这个不加上应该编译不过,主要是防止对应的远程接口没有定义,导致APP调用出错;
2、构造函数有两个参数,一个是Handler,一个是IWrtdevManager (继承于android.os.IInterface)
在WrtdevManager.java文件中有用到IWrtdevManager,这是通过编译aidl文件生成,定义的.aidl文件如下:
package com.wrtsz.api;
interface IWrtdevManager
{
int getMicroWaveState();
byte[] getIcCardNo();
int openLed(int opIndex);
int openDoor();
}
需要注意的是:.aidl中的接口必须跟.java中的接口保持一致,否则会编译不过。另外就是aidl语言的语法跟Java语言略有不同,且传递的参数必须是Java基本类型或者是其他实现Parcelable
接口的类,这里不再详述,可以参考官方文档:https://developer.android.com/guide/components/aidl?hl=zh-cn
上面虽然定义了两个文件,但是默认情况下,编译Android的时候是不会编译到这两个文件的,如果要用编译到对应的文件,需要修改frameworks/base/Android.mk文件,在对应的LOCAL_SRC_FILES宏中添加对应的文件即可,相应的patch文件如下:
packages/services/Proxy/com/android/net/IProxyCallback.aidl \
packages/services/Proxy/com/android/net/IProxyPortListener.aidl \
core/java/android/os/IDisplayDeviceManagementService.aidl \
+ core/java/com/wrtsz/api/IWrtdevManager.aidl \
+ core/java/com/wrtsz/api/WrtdevManager.java
# FRAMEWORKS_BASE_JAVA_SRC_DIRS comes from build/core/pathmap.mk
LOCAL_AIDL_INCLUDES += $(FRAMEWORKS_BASE_JAVA_SRC_DIRS)
清空之后在Android根目录编译,发现会报错:
see build/core/apicheck_msg_current.txt
******************************
You have tried to change the API from what has been previously approved.
To make these errors go away, you have two choices:
1) You can add "@hide" javadoc comments to the methods, etc. listed in the
errors above.
2) You can update current.txt by executing the following command:
make update-api
To submit the revised current.txt to the main Android repository,
you will need approval.
******************************
熟悉Android SDK开发的就比较清楚原因,因为我们添加了系统接口,需要先make update-api,执行完该命令后,发现frameworks/base/api/current.txt文件有更新,可以看到,文件中增加了一个WRTSZ_SERVICE 的常量和一个com.wrtsz.api的package,该package中就有对应的接口声明。
field public static final java.lang.String WIFI_P2P_SERVICE = "wifip2p";
field public static final java.lang.String WIFI_SERVICE = "wifi";
field public static final java.lang.String WINDOW_SERVICE = "window";
field public static final java.lang.String WRTSZ_SERVICE = "wrtsz";
}
public class ContextWrapper extends android.content.Context {
@@ -39532,6 +39533,24 @@ package com.android.internal.util {
}
package com.wrtsz.api {
public abstract interface IWrtdevManager implements android.os.IInterface {
method public abstract byte[] getIcCardNo() throws android.os.RemoteException;
method public abstract int getMicroWaveState() throws android.os.RemoteException;
method public abstract int openDoor() throws android.os.RemoteException;
method public abstract int openLed(int) throws android.os.RemoteException;
}
public static abstract class IWrtdevManager.Stub extends android.os.Binder implements com.wrtsz.api.IWrtdevManager {
ctor public IWrtdevManager.Stub();
method public android.os.IBinder asBinder();
method public static com.wrtsz.api.IWrtdevManager asInterface(android.os.IBinder);
method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
}
}
接着就可以敲make -j16命令直接编译了,编译完成之后,在./out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/wrtsz/api目录下会生成一个IWrtdevManager.java文件,对应的内容如下:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: frameworks/base/core/java/com/wrtsz/api/IWrtdevManager.aidl
*/
package com.wrtsz.api;
public interface IWrtdevManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.wrtsz.api.IWrtdevManager
{
private static final java.lang.String DESCRIPTOR = "com.wrtsz.api.IWrtdevManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.wrtsz.api.IWrtdevManager interface,
* generating a proxy if needed.
*/
public static com.wrtsz.api.IWrtdevManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.wrtsz.api.IWrtdevManager))) {
return ((com.wrtsz.api.IWrtdevManager)iin);
}
return new com.wrtsz.api.IWrtdevManager.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getMicroWaveState:
{
data.enforceInterface(DESCRIPTOR);
int _result = this.getMicroWaveState();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_getIcCardNo:
{
data.enforceInterface(DESCRIPTOR);
byte[] _result = this.getIcCardNo();
reply.writeNoException();
reply.writeByteArray(_result);
return true;
}
case TRANSACTION_openLed:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _result = this.openLed(_arg0);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_openDoor:
{
data.enforceInterface(DESCRIPTOR);
int _result = this.openDoor();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_getFireAlarmStatus:
{
data.enforceInterface(DESCRIPTOR);
int _result = this.getFireAlarmStatus();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_getMagnetometerStatus:
{
data.enforceInterface(DESCRIPTOR);
int _result = this.getMagnetometerStatus();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_getTamperStatus:
{
data.enforceInterface(DESCRIPTOR);
int _result = this.getTamperStatus();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.wrtsz.api.IWrtdevManager
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public int getMicroWaveState() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getMicroWaveState, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public byte[] getIcCardNo() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
byte[] _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getIcCardNo, _data, _reply, 0);
_reply.readException();
_result = _reply.createByteArray();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public int openLed(int opIndex) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(opIndex);
mRemote.transact(Stub.TRANSACTION_openLed, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public int openDoor() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_openDoor, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public int getFireAlarmStatus() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getFireAlarmStatus, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public int getMagnetometerStatus() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getMagnetometerStatus, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public int getTamperStatus() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getTamperStatus, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getMicroWaveState = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getIcCardNo = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_openLed = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_openDoor = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
static final int TRANSACTION_getFireAlarmStatus = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
static final int TRANSACTION_getMagnetometerStatus = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
static final int TRANSACTION_getTamperStatus = (android.os.IBinder.FIRST_CALL_TRANSACTION + 6);
}
public int getMicroWaveState() throws android.os.RemoteException;
public byte[] getIcCardNo() throws android.os.RemoteException;
public int openLed(int opIndex) throws android.os.RemoteException;
public int openDoor() throws android.os.RemoteException;
public int getFireAlarmStatus() throws android.os.RemoteException;
public int getMagnetometerStatus() throws android.os.RemoteException;
public int getTamperStatus() throws android.os.RemoteException;
}
文件的头两行注释就写清楚了:1、这个文件是自动生成的,不要自行修改;2、其对应的原始文件为frameworks/base/core/java/com/wrtsz/api/IWrtdevManager.aidl
* This file is auto-generated. DO NOT MODIFY.
* Original file: frameworks/base/core/java/com/wrtsz/api/IWrtdevManager.aidl
以上完成之后,我们最终要输出给第三方一个jar包,该文件最终生成在out/target/common/obj/JAVA_LIBRARIES/framework_intermediates目录下,我们可以ls -l看一下,目录下的classes.jar文件就是我们要找的文件。
当然,细心的童鞋可能会发现classes.jar、class-full-debug.jar、classes-jarjar.jar其实是三个同样的文件,校验md5值就可以证明。
然后把对应的 classes.jar文件release给第三方,替换原生的android.jar,就可以愉快地调用我们的WrtdevManager接口了。
以上部分完成后,第三方APP调用接口的时候,会发现编译不会报错,但是运行时会报RemoteException的异常,因为我们还没有定义对应的service。
可以在frameworks/opt目录下创建一个vendor/wrtsz/java/com/wrtsz/server目录,在该目录下首先新建一个WrtdevService类,该类继承自SystemService类,对应的代码如下:
package com.wrtsz.server;
import android.content.Context;
import android.util.Log;
import com.android.server.SystemService;
public final class WrtdevService extends SystemService {
private static final String TAG = "csh_debug_wrtsz";
final WrtdevServiceImpl mImpl;
public WrtdevService(Context context) {
super(context);
mImpl = new WrtdevServiceImpl(context);
}
@Override
public void onStart() {
Log.i(TAG, "Registering service " + Context.WRTSZ_SERVICE);
publishBinderService(Context.WRTSZ_SERVICE, mImpl);
}
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
mImpl.start();
}
}
}
可以看到,在WrtdevService中,我们调用了WrtdevServiceImpl 类,这个类才是真正干活儿的,说白了WrtdevService类不过是个马甲,方便SystemServer统一管理。Android5.1是这种架构,之前Android4.4中可以不需要定义WrtdevService,直接定义WrtdevServiceImpl类,其代码如下:
package com.wrtsz.server;
import android.util.Log;
import android.content.Context;
import com.wrtsz.api.IWrtdevManager;
public class WrtdevServiceImpl extends IWrtdevManager.Stub {
private static final String TAG = "WrtdevServiceImpl";
public Context mContext;
private final WrtdevNative mNative;
public WrtdevServiceImpl(Context context) {
Log.i(TAG, "Creating WrtdevServiceImpl");
mContext=context;
mNative=new WrtdevNative();
}
public void start()
{
Log.i(TAG, "Starting Wrtdev service");
}
public int getMicroWaveState()
{
return mNative.getMicroWaveState();
}
public byte[] getIcCardNo()
{
return mNative.getIcCardNo();
}
public int openLed(int opIndex)
{
return mNative.openLed(opIndex);
}
public int openDoor()
{
return mNative.openDoor();
}
}
WrtdevServiceImpl类中实现了所有WrtdevManager中定义的接口,它通过一个native类,最终调用C层接口,由于跟业务有关,这里不再赘述。
然后就是在同目录下新建一个Android.mk,对应的内容如下:
LOCAL_CFLAGS += -Wno-unused-parameter -Wno-int-to-pointer-cast
LOCAL_CFLAGS += -Wno-maybe-uninitialized -Wno-parentheses
LOCAL_CPPFLAGS += -Wno-conversion-null
include $(CLEAR_VARS)
LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/java
LOCAL_SRC_FILES := $(call all-java-files-under, java) \
$(call all-Iaidl-files-under, java) \
$(call all-logtags-files-under, java)
LOCAL_JAVA_LIBRARIES := services
LOCAL_MODULE := wrtsz-service
include $(BUILD_JAVA_LIBRARY)
编译之后会在./out/target/product/rk3288/system/framework/目录下生成一个wrtsz-service.jar文件。
系统service的注册统一在frameworks/base/core/java/android/app/ContextImpl.java 中,调用registerService方法。
import java.util.ArrayList;
import java.util.HashMap;
import com.wrtsz.api.WrtdevManager;
import com.wrtsz.api.IWrtdevManager;
class ReceiverRestrictedContext extends ContextWrapper {
ReceiverRestrictedContext(Context base) {
super(base);
@@ -648,6 +651,16 @@ class ContextImpl extends Context {
}});
registerService(WRTSZ_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(WRTSZ_SERVICE);
IWrtdevManager service = IWrtdevManager.Stub.asInterface(b);
return new WrtdevManager(service, ctx.mMainThread.getHandler());
}});
registerService(WINDOW_SERVICE, new ServiceFetcher() {
Display mDefaultDisplay;
public Object getService(ContextImpl ctx) {
这里的WRTSZ_SERVICE常量定义在 frameworks/base/core/java/android/content/Context.java 中,方便上层APP通过addService方法调用时,直接传这个常量。
注册完服务之后,需要启动服务,需要在 frameworks/base/services/java/com/android/server/SystemServer.java中添加,调用startService启动,直接传对应的类全名即可。
try {
Slog.i("csh_debug_wrtsz", "WrtszService");
mSystemServiceManager.startService("com.wrtsz.server.WrtdevService");
}catch (Throwable e) {
reportWtf("start WrtszService error ", e);
}
以上添加完之后,如果要正常编译运行,还需要在device/rockchip/common/device.mk中添加几行代码:
PRODUCT_PACKAGES += \
wrtsz-service
PRODUCT_SYSTEM_SERVER_JARS += \
wrtsz-service
注意一定要在PRODUCT_SYSTEM_SERVER_JARS宏中添加自定义的service,否则系统跑起来之后,自定义的service代码其实没有执行。
以上就是自定义Android服务和接口的全部内容,不同的芯片商和不同的Android版本,可能略有不同,但是整体架构差不多。