Android Binder分析五:Java service的獲取和呼叫
前面介紹過註冊Java Service的流程,這一章我們來看應用程式如何呼叫Java Service的介面。
Java Service的獲取
在Java Service當中,會經常使用到AIDL工具,AIDL在google官方文件的解釋是:"AIDL (Android Interface Definition Language) is similar to other IDLs
you might have worked with. It allows you to define the programming interface that both the client and service agree upon
in order to communicate with each other using interprocess communication (IPC)
interface IWifiManager { List<WifiConfiguration> getConfiguredNetworks(); int addOrUpdateNetwork(in WifiConfiguration config); boolean removeNetwork(int netId); boolean enableNetwork(int netId, boolean disableOthers); boolean disableNetwork(int netId); boolean pingSupplicant(); void startScan(in WorkSource ws); List<ScanResult> getScanResults(String callingPackage); void disconnect(); void reconnect(); void reassociate();
這裡定義了一系列的函式介面,在AIDL中除了支援Java原始型別資料(int、long、char、boolean等),還支援String、CharSequence、list和map。進過AIDL工具的轉換,就可以將上面的aidl檔案生成一個Java檔案,其內容大致如下:
public interface IWifiManager extends android.os.IInterface { public static abstract class Stub extends android.os.Binder implements android.net.wifi.IWifiManager { private static final java.lang.String DESCRIPTOR = "android.net.wifi.IWifiManager"; public Stub() { this.attachInterface(this, DESCRIPTOR); } public static android.net.wifi.IWifiManager asInterface( android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = (android.os.IInterface) obj .queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof android.net.wifi.IWifiManager))) { return ((android.net.wifi.IWifiManager) iin); } return new android.net.wifi.IWifiManager.Stub.Proxy(obj); } 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) { } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements android.net.wifi.IWifiManager { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } }
IWifiManager.java中主要包含三個類:IWifiManager類、IWifiManager.Stub類、IWifiManager.Stub.Proxy類,WifiSerive就是繼承於IWifiManager.Stub類。前面我們已經看過它的關係了,下面來看一般獲取WifiService的方法:
IBinder b = ServiceManager.getService(WIFI_SERVICE);
IWifiManager service = IWifiManager.Stub.asInterface(b);
return new WifiManager(ctx.getOuterContext(), service);
這裡呼叫ServiceManager的getService方法,WIFI_SERVICE就是"wifi"字串:
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return getIServiceManager().getService(name);
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
sCache用於快取所有使用過的Java Service,它有ActivityManagerService在fork程序的時候拷貝進來,我們以後分析到AMS的時候再來看sCache,這裡先假設sCache並沒有我們想要的"wifi" service。然後呼叫getIServiceManager()的getService方法。前面分析過getIServiceManager()其實最後就是返回ServiceManagerProxy(BinderProxy),所以這裡呼叫ServiceManagerProxy的getService方法:
public IBinder getService(String name) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IServiceManager.descriptor);
data.writeString(name);
mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
IBinder binder = reply.readStrongBinder();
reply.recycle();
data.recycle();
return binder;
}
首先獲取兩個Parcel物件,然後向data中寫入Strict mode和"android.os.IServiceManager",然後再寫入想要獲取的service名字,即“wifi”。再呼叫mRemote.transact方法,這裡的實現在android_util_Binder.cpp中,我們前面一章分析過,這裡直接呼叫BpBinder(0)的transact方法,將GET_SERVICE_TRANSACTION命令發往binder驅動。我們來看Java層如何從Parcel中讀取想要的Servcie:
public final IBinder readStrongBinder() {
return nativeReadStrongBinder(mNativePtr);
}
static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jint nativePtr)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
return javaObjectForIBinder(env, parcel->readStrongBinder());
}
return NULL;
}
我們前面在分析Native Service的獲取時曾經說過binder驅動首先判斷ref->node->proc == target_proc,即註冊的程序和現在獲取MediaPlayerService的程序是否是同一個,如果是,則改寫fp->type為BINDER_TYPE_BINDER,並設定fp->binder為binder->getWeakRefs(),fp->cookie等於binder->local本身。如果不是在同一個程序(這也是大多數case的狀況),則首先呼叫binder_get_ref_for_node為獲取MediaPlayerService的程序分配一個新的binder_ref結構,這裡的binder id值可能不是之前註冊的binder id值了(因為不在同一個程序,desc是往上增長的),然後設定fp->handle為新的desc值。首先來看Native層的Parcel的readStrongBinder方法:
sp<IBinder> Parcel::readStrongBinder() const
{
sp<IBinder> val;
unflatten_binder(ProcessState::self(), *this, &val);
return val;
}
status_t unflatten_binder(const sp<ProcessState>& proc,
const Parcel& in, sp<IBinder>* out)
{
const flat_binder_object* flat = in.readObject(false);
if (flat) {
switch (flat->type) {
case BINDER_TYPE_BINDER:
*out = static_cast<IBinder*>(flat->cookie);
return finish_unflatten_binder(NULL, *flat, in);
case BINDER_TYPE_HANDLE:
*out = proc->getStrongProxyForHandle(flat->handle);
return finish_unflatten_binder(
static_cast<BpBinder*>(out->get()), *flat, in);
}
}
return BAD_TYPE;
}
這裡分兩種情況,一是註冊WifiService的程序和獲取WifiService的程序是同一個時(這個比較經常,因為註冊WifiService是在systemserver當中,systemserver會註冊很多其它的Service,這些Service會互相呼叫)。這時type就是BINDER_TYPE_BINDER,然後通過static_cast將cookie處儲存的指標直接轉換為前面註冊的JavaBBinder;二是註冊WifiSerivce的程序和獲取WifiService的程序不是同一個時,這裡呼叫getStrongProxyForHandle重新返回一個BpBinder(handle id)。回到上面的android_os_Parcel_readStrongBinder方法,這裡再呼叫javaObjectForIBinder對IBinder物件做一層封裝:
jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
{
if (val == NULL) return NULL;
if (val->checkSubclass(&gBinderOffsets)) {
}
AutoMutex _l(mProxyLock);
jobject object = (jobject)val->findObject(&gBinderProxyOffsets);
if (object != NULL) {
jobject res = jniGetReferent(env, object);
if (res != NULL) {
ALOGV("objectForBinder %p: found existing %p!\n", val.get(), res);
return res;
}
LOGDEATH("Proxy object %p of IBinder %p no longer in working set!!!", object, val.get());
android_atomic_dec(&gNumProxyRefs);
val->detachObject(&gBinderProxyOffsets);
env->DeleteGlobalRef(object);
}
object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);
if (object != NULL) {
env->SetIntField(object, gBinderProxyOffsets.mObject, (int)val.get());
val->incStrong((void*)javaObjectForIBinder);
jobject refObject = env->NewGlobalRef(
env->GetObjectField(object, gBinderProxyOffsets.mSelf));
val->attachObject(&gBinderProxyOffsets, refObject,
jnienv_to_javavm(env), proxy_cleanup);
sp<DeathRecipientList> drl = new DeathRecipientList;
drl->incStrong((void*)javaObjectForIBinder);
env->SetIntField(object, gBinderProxyOffsets.mOrgue, reinterpret_cast<jint>(drl.get()));
android_atomic_inc(&gNumProxyRefs);
incRefsCreated(env);
}
return object;
}
前一章裡面分析過這個函式,這裡會構造一個BinderProxy物件,並將當前的IBinder(可能是JavaBBinder,也可能是BpBinder)與BinderProxy繫結起來。然後將BinderProxy物件返回給獲取Service的程序。接著呼叫IWifiManager service = IWifiManager.Stub.asInterface(b),它的實現如下:
public static android.net.wifi.IWifiManager asInterface(
android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = (android.os.IInterface) obj
.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof android.net.wifi.IWifiManager))) {
return ((android.net.wifi.IWifiManager) iin);
}
return new android.net.wifi.IWifiManager.Stub.Proxy(obj);
}
這裡的obj是BinderProxy物件,它的queryLocalInterface預設返回NULL,所以這裡會先構造一個IWifiManager.Stub.Proxy並返回。最後通過IWifiManager.Stub.Proxy構造一個WifiManager物件給應用層使用。
Java Service的呼叫
我們來看一下WifiManager中一個簡單的API使用: public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
try {
mService.setWifiApEnabled(wifiConfig, enabled);
return true;
} catch (RemoteException e) {
return false;
}
}
這裡的mService就是上面構造的IWifiManager.Stub.Proxy物件,來看它的setWifiApEnabled方法:
public boolean setWifiApEnabled(
android.net.wifi.WifiConfiguration wifiConfig,
boolean enable) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
boolean _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((wifiConfig != null)) {
_data.writeInt(1);
wifiConfig.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
_data.writeInt(((enable) ? (1) : (0)));
mRemote.transact(Stub.TRANSACTION_setWifiApEnabled, _data,
_reply, 0);
_reply.readException();
_result = (0 != _reply.readInt());
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
首先還是獲取兩個Parcel物件,一個用於儲存傳送到WifiService的資料;一個用於獲取WifiService的回覆。並向data中寫入strict mode和"android.net.wifi.IWifiManager",再寫入兩個引數。最後呼叫mRemote.transact方法。我們知道這裡的mRemote就是前面構造的BinderProxy物件,所以來看它的transact方法,它的實現在android_util_Binder.cpp
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{
if (dataObj == NULL) {
}
Parcel* data = parcelForJavaObject(env, dataObj);
if (data == NULL) {
return JNI_FALSE;
}
Parcel* reply = parcelForJavaObject(env, replyObj);
if (reply == NULL && replyObj != NULL) {
return JNI_FALSE;
}
IBinder* target = (IBinder*)
env->GetIntField(obj, gBinderProxyOffsets.mObject);
if (target == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", "Binder has been finalized!");
return JNI_FALSE;
}
status_t err = target->transact(code, *data, reply, flags);
if (err == NO_ERROR) {
return JNI_TRUE;
} else if (err == UNKNOWN_TRANSACTION) {
return JNI_FALSE;
}
signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/);
return JNI_FALSE;
}
這裡也要分兩種情況討論,一種是在同一個程序中,這裡的target就是JavaBBinder;如果在不同程序中,這裡的target就是BpBinder。首先來看在同一個程序的情況,呼叫JavaBBinder的transact方法,因為JavaBBinder是繼承於BBinder,所以直接到BBinder的transact方法中:
status_t BBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
data.setDataPosition(0);
status_t err = NO_ERROR;
switch (code) {
case PING_TRANSACTION:
reply->writeInt32(pingBinder());
break;
default:
err = onTransact(code, data, reply, flags);
break;
}
if (reply != NULL) {
reply->setDataPosition(0);
}
return err;
}
直接呼叫onTransact方法,當然這裡的onTransact方法是JavaBBinder重寫後的。當不同同一個程序中呼叫時,target就是BpBinder,所以呼叫BpBinder的transact方法,最後通過ioctrl傳送命令給註冊binder驅動中的JavaBBinder(WifiService的包裝),同樣還是會呼叫到JavaBBinder的onTransact方法,只是多了一個跨程序的資料傳遞過程。
virtual status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0)
{
JNIEnv* env = javavm_to_jnienv(mVM);
IPCThreadState* thread_state = IPCThreadState::self();
const int strict_policy_before = thread_state->getStrictModePolicy();
thread_state->setLastTransactionBinderFlags(flags);
jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
code, (int32_t)&data, (int32_t)reply, flags);
jthrowable excep = env->ExceptionOccurred();
const int strict_policy_after = thread_state->getStrictModePolicy();
if (strict_policy_after != strict_policy_before) {
thread_state->setStrictModePolicy(strict_policy_before);
set_dalvik_blockguard_policy(env, strict_policy_before);
}
jthrowable excep2 = env->ExceptionOccurred();
if (excep2) {
report_exception(env, excep2,
"*** Uncaught exception in onBinderStrictModePolicyChange");
/* clean up JNI local ref -- we don't return to Java code */
env->DeleteLocalRef(excep2);
}
if (code == SYSPROPS_TRANSACTION) {
BBinder::onTransact(code, data, reply, flags);
}
return res != JNI_FALSE ? NO_ERROR : UNKNOWN_TRANSACTION;
}
這裡主要就是呼叫Binder.java的execTransact來處理請求:
private boolean execTransact(int code, int dataObj, int replyObj,
int flags) {
Parcel data = Parcel.obtain(dataObj);
Parcel reply = Parcel.obtain(replyObj);
boolean res;
try {
res = onTransact(code, data, reply, flags);
} catch (RemoteException e) {
if ((flags & FLAG_ONEWAY) != 0) {
Log.w(TAG, "Binder call failed.", e);
}
reply.setDataPosition(0);
reply.writeException(e);
res = true;
} catch (RuntimeException e) {
if ((flags & FLAG_ONEWAY) != 0) {
Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
}
reply.setDataPosition(0);
reply.writeException(e);
res = true;
} catch (OutOfMemoryError e) {
Log.e(TAG, "Caught an OutOfMemoryError from the binder stub implementation.", e);
RuntimeException re = new RuntimeException("Out of memory", e);
reply.setDataPosition(0);
reply.writeException(re);
res = true;
}
reply.recycle();
data.recycle();
return res;
}
}
這裡直接呼叫WIfiService的onTransact方法,它的實現在IWifiManager.Stub中:
public boolean onTransact(int code, android.os.Parcel data,
android.os.Parcel reply, int flags)
throws android.os.RemoteException {
switch (code) {
case TRANSACTION_setWifiApEnabled: {
data.enforceInterface(DESCRIPTOR);
android.net.wifi.WifiConfiguration _arg0;
if ((0 != data.readInt())) {
_arg0 = android.net.wifi.WifiConfiguration.CREATOR
.createFromParcel(data);
} else {
_arg0 = null;
}
boolean _arg1;
_arg1 = (0 != data.readInt());
boolean _result = this.setWifiApEnabled(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(((_result) ? (1) : (0)));
return true;
}
首先從Parcel中讀出WifiConfiguration和enable兩個引數,然後呼叫WifiServcie的setWifiApEnabled方法,並把執行結果寫入到reply中。如果不在同一個程序中呼叫,最後通過IPCThreadState的sendReply方法將結果返回給呼叫者。到這裡,一次Java Servcie的呼叫就完成了。
相關推薦
Android Binder分析五:Java service的獲取和呼叫
前面介紹過註冊Java Service的流程,這一章我們來看應用程式如何呼叫Java Service的介面。 Java Service的獲取 在Java Service當中,會經常使用到AIDL工具,AIDL在google官方文件的解釋是:"AIDL (Android In
擁抱 Android Studio 之五:Gradle 插件開發
變量 -h min gui root artifact direct 抽象 path 實踐出真知 筆者有位朋友。每次新學一門語言,都會用來寫一個貪吃蛇遊戲,以此來檢驗自己學習的成果。筆者也有相似體會。所謂紙上得來終覺淺
【轉載】Android Bug分析系列:第三方平臺安裝app啟動後,home鍵回到桌面後點擊app啟動時會再次啟動入口類bug的原因剖析
特殊 返回 androidm android系統 圖片 管理 相關 OS 簡便 前言 前些天,測試MM發現了一個比較奇怪的bug。 具體表現是: 1、將app包通過電腦QQ傳送到手機QQ上面,點擊安裝,安裝後選擇打開app (此間的應用邏輯應該是要觸發 【閃屏頁
微信小程序:java後臺獲取openId
+= rspec post main 接口 common https output rgs 一、功能描述 openId是某個微信賬戶對應某個小程序或者公眾號的唯一標識,但openId必須經過後臺解密才能獲取(之前實現過前臺解密,可是由於微信小程序的種種限制,前臺解密無法在小
Android JNI 學習(五):JNI 接口整理 — References Api
-a delet 內存 引用 5.4 cap 兼容 error span 1. NewGlobalRef(創建全局引用) jobjectNewGlobalRef(JNIEnv *env, jobject obj); 創建 obj 參數所引用對象的新全局引用。obj 參數
Android Binder分析
Binder通訊模型 Binder的優勢 實現方式 Binder使用Client-Server通訊方式:一個程序作為Server提供諸如視訊/音訊解碼,視訊捕獲,地址本查詢,網路連線等服務;多個程序作為Client向Server發起服務請求,獲得所需要的
Java:java爬蟲獲取動態網頁的資料
說明: 只是分享一種解決方案,程式碼以及部分截圖不方便貼出,請諒解! 前段時間一直在研究爬蟲,抓取網路上的特定的資料,如果只是靜態網頁就是再簡單不過了,直接使用Jsoup : Document doc = Jsoup.connect(url).timeout(2000).
Android單元測試(五):網路介面測試
溫馨提示:如果你不太熟悉單元測試,可以先看下之前四篇基礎框架使用。便於你更好的理解下面的內容。 在平日的開發中,我們用後臺寫好給我們介面去獲取資料。雖然我們有一些請求介面的工具,可以快速的拿到返回資料。但是在一些異常情況的處理上就不太方便了。我列出以下
《Java 原始碼分析》:Java NIO 之 Selector(第一部分Selector.open())
《Java 原始碼分析》 :Java NIO 之 Selector(第一部分Selector.open()) 關於Selector類主要涉及兩個重要的方法,如下: 1、Selector.open() 2、select() 由於篇幅限制,這篇主要從
android Toast提示異常:java.lang.RuntimeException: Can't create handler inside thread that has not called
android Toast提示異常:java.lang.RuntimeException: Can't create handler inside thread that has not
手寫spring二:Java反射獲取類和物件資訊全解析
反射在這裡的作用就是知道全路徑 在框架啟動的時候把類例項化 然後設定到@service 和@Autowired裡面 所以要了解這東西怎麼用的 1. 什麼是類物件 類物件,就是用於描述這種類,都有什麼屬性,什麼方法的 2. 獲取類物件 獲取類物件有3種方式 (1). Class.f
擁抱 Android Studio 之五:Gradle 外掛開發
轉載處:http://geek.csdn.net/news/detail/64058 實踐出真知 筆者有位朋友,每次新學一門語言,都會用來寫一個貪吃蛇遊戲,以此來檢驗自己學習的成果。筆者也有類似體會。所謂紙上得來終覺淺,絕知此事要躬行。這一章,筆者將以開發和釋出一
Android NDK 執行錯誤:java.lang.UnsatisfiedLinkError: Couldn't load XXX indLibrary returned null
load hello-jni from loader dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.hellojni-1.apk"],nativeLibraryDirectories=[/data/app-
Android記憶體分析工具:Memory Profiler
一、前言 我們知道,Android系統檢測到app有不再使用物件時,就會進行記憶體回收相關的工作。 儘管Android檢測無用物件、回收記憶體的方法在不斷改進, 但在目前所有的Android版本中,進行上述工作時,系統仍需要短暫地停止app的執行。 在大
【深入Java虛擬機器】之五:Java垃圾收集機制
物件引用 Java中的垃圾回收一般是在Java堆中進行,因為堆中幾乎存放了Java中所有的物件例項。談到Java堆中的垃圾回收,自然要談到引用。在JDK1.2之前,Java中的引用定義很很純粹:如果reference型別的資料中儲存的數值代表的是另外一塊
ABP原始碼分析五:ABP初始化全過程
ABP在初始化階段做了哪些操作,前面的四篇文章大致描述了一下。 為個更清楚的描述其脈絡,做了張流程圖以輔助說明。其中每一步都涉及很多細節,難以在一張圖中全部表現出來。每一步的細節(會涉及到較多介面,類,呼叫關係,步驟流程什麼的)會在後面的文章中通過其他圖和文字詳細描述。其實如果仔細分析Abp原始碼的話,會發現
(2)Solr:Java後臺獲取Solr查詢資訊
這裡介紹兩種方法:1.Solrj,2.httpClient httpGet 1.Solrj 首先需要下載Solrj相關的jar包,其實在Solr的下載包中就已經包含了Solrj jar包和依賴的jar包,具體位置: 依賴:Solr解壓檔案\dist\sol
AJAX教程系列五:非同步資料獲取與定時器
window.onload = function() { var oBtn = document.getElementById('btn'); oBtn.onclick = function() { /*ajax({ url : 'getN
Android PackageManagerService分析三:解除安裝APK
這一章我們介紹APK的解除安裝過程,從前一章分析安裝APK的過程,我們應該大致瞭解這裡的解除安裝的過程如下: 1.從PMS的內部結構上刪除acitivity、service、provider等資訊 2.刪除code、library和resource等資訊 3.呼叫insta
Android Binder 分析——資料傳遞者(Parcel)
前面 binder 原理和通訊模型中在介面實現部分(Bp 和 Bn)中應該看到很多地方都有使用 parcel。這個 android 專門設計用來跨程序傳遞資料的,實現在 native,java 層有介面(基本上是 jni 馬甲)。照例先說下原始碼位置(4.4 的): 12