赞
踩
本博文配套的硬件是瑞芯微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,对应的内容如下:
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版本,可能略有不同,但是整体架构差不多。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。