❶ 如何在android 系統 C/C++ 層中添加 log 信息
#include 並且在 函數中添加如下 log 輸出信息(如藍色字體顯示):status_t MediaRecorder::setCamera(const sp& camera){LOGV("setCamera(%p)", camera.get()); if(mMediaRecorder == NULL) { LOGE("media recorder is not initialized yet"); return INVALID_OPERATION;}if (!(mCurrentState & MEDIA_RECORDER_IDLE)) { LOGE("setCamera called in an invalid state(%d)", mCurrentState); return INVALID_OPERATION;}status_t ret = mMediaRecorder->setCamera(camera); if (OK != ret) { LOGV("setCamera failed: %d", ret); mCurrentState = MEDIA_RECORDER_ERROR; 並且宏定義一下原始的 log 輸出函數 __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG, __VA_ARGS__); 或者__android_log_write(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__);這樣就不需要在打 log 的時候敲入那麼多字元了宏定義如下:#define LOG_TAG "MediaRecorder"#define LOG(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG, __VA_ARGS__) 當然,你也可以只列印點字元信息LOG("InitFbImage");Log 信息查看:所有的 log 信息都可以通過控制台輸出。
❷ android bluetooth hid協議的開發求助
Android Bluetooth HID實現詳解
Android 關於藍牙的部分使用的是BlueZ協議棧。但是直到目前2.3.3都沒有擴展HID的profile,只是實現了最基本的Handset和d2dp的profile,所以我們的工作涉及到從應用到jni三層的修改,具體修改文件如圖所示,綠色表示新建的類,橙色表示修改的類。
一. 本地層
路徑:framework/base/core/jni/
參照android_server_BluetoothA2dpService.cpp新建android_server_bluetoothHidServer.cpp。該類中主要是通過dbus對bluez協議棧的訪問,dbus 的通用方法都在android_bluetooth_common.cpp中實現,我們做的僅僅是通過dbus_func_args_async調用到bluez提供的input介面。
主要實現以下兩個方法函數:
static jboolean connectSinkNative(JNIEnv *env, jobject object, jstring path) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
if (nat) {
const char *c_path = env->GetStringUTFChars(path, NULL);
bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat,
c_path, "org.bluez.Input", "Connect",
DBUS_TYPE_INVALID);
env->ReleaseStringUTFChars(path, c_path);
return ret ? JNI_TRUE : JNI_FALSE;
}
#endif
return JNI_FALSE;
}
static jboolean disconnectSinkNative(JNIEnv *env, jobject object,
jstring path) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
if (nat) {
const char *c_path = env->GetStringUTFChars(path, NULL);
bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat,
c_path, "org.bluez.Input", "Disconnect",
DBUS_TYPE_INVALID);
env->ReleaseStringUTFChars(path, c_path);
return ret ? JNI_TRUE : JNI_FALSE;
}
#endif
return JNI_FALSE;
}
這里要注意將該文件添加到AndroidRuntime.cpp和Android.mk中,否則不會編譯到動態庫中。
此部分編譯後最終生成libandroid_runtime.so並替換到system/libs下
二.Framework的java部分
路徑framework/base/java/android/server/中添加BluetoothHidService.java文件
路徑framework/base/java/android/bluetooth/中添加BluetoothHid.java和IBluetoothHid.aidl文件。
interface IBluetoothHid {
boolean connect(in BluetoothDevice device);
boolean disconnect(in BluetoothDevice device);
int getState(in BluetoothDevice device);
boolean setPriority(in BluetoothDevice device, int priority);
int getPriority(in BluetoothDevice device);
}
BluetoothHid.java中主要的兩個方法connect和disconnect間接地通過aidl訪問BluetoothHidService。這里主要是實現跨進程並為上層提供可直接訪問的方法。
由此framework的主要部分打包生成framework.Jar並最終部署到system/framework里。
三.應用(Settings.apk)
最後需要修改應用部分,應用部分的修改點比較分散,不想框架層那樣整塊模仿A2DP的樣子那麼方便,但也不是說jni部分有多麼容易。反而對於我這種對C語言不熟悉的人來說,修改jni是最頭疼得事了。好在藍牙HID這部分框架層的修改都是整塊進行的,理解上還算比價容易。
總的來說在Settings.apk中要修改的文件主要是這么幾個:
LocalBluetoothProfileManager.java這里主要提供一個HID的profile以便應用層訪問。建一個HIDProfile的class調用framework中的BluetoothHID。實際上就是通過bender機制調用了BluetoothHidService。
CashedBluetoothDevice中添加顯示藍牙鍵盤的圖標,BluetoothPairingDialog中則需要添加一段藍牙配對驗證處理的代碼,我是參照i9000中先彈出一個隨機數,然後在鍵盤中敲入相同的隨機數即配對成功,具體實現如下:
// HID
if (isDeviceKeyboard(mDevice)) {
String pin = String.format("%06d", Long.valueOf(Math
.abs(new Random().nextLong() % 1000000L)));
mPairingView.setVisibility(View.GONE);
messageView.setText(getString(
R.string.bluetooth_enter_keyboard_pin_msg, pin, name));
byte[] bytePin = BluetoothDevice.convertPinToBytes(pin);
if (bytePin != null) {
mDevice.setPin(bytePin);
}
}
……
}
轉載
❸ Android系統怎麼利用利用Java反射技術阻止通過按鈕關閉對話框(AlertDialog)
眾所周知,AlertDialog類用於顯示對話框。關於AlertDialog的基本用法在這里就不詳細介紹了,網上有很多,讀者可以自己搜索。那麼本文要介紹的是如何隨心所欲地控制AlertDialog。
現在我們來看看第一個需求:如果某個應用需要彈出一個對話框。當單擊「確定「按鈕時完成某些工作,如果這些工作失敗,對話框不能關閉。而當成功完成工作後,則關閉對話框。當然,無論何程度情況,單擊「取消」按鈕都會關閉對話框。
這個需求並不復雜,也並不過分(雖然我們可以自己弄個Activity來完成這個工作,也可在View上自己放按鈕,但這顯示有些大炮打蚊子了,如果對話框上只有一行文本,費這么多勁太不值了)。但使用過AlertDialog的讀者都知道,無論單擊的哪個按鈕,無論按鈕單擊事件的執行情況如何,對話框是肯定要關閉的。也就是說,用戶無法控制對話框的關閉動作。實際上,關閉對話框的動作已經在Android SDK寫死了,並且未給使用者留有任何介面。但我的座右銘是「宇宙中沒有什麼是不能控制的」。
既然要控制對放框的關閉行為,首先就得分析是哪些類、哪些代碼使這個對話框關閉的。進入AlertDialog類的源代碼。在AlertDialog中只定義了一個變數:mAlert。這個變數是AlertController類型。AlertController類是Android的內部類,在com.android.internal.app包中,無法通過普通的方式訪問。也無法在Eclipse中通過按Ctrl鍵跟蹤進源代碼。但可以直接在Android源代碼中找到AlertController.java。我們再回到AlertDialog類中。AlertDialog類實際上只是一個架子。象設置按鈕、設置標題等工作都是由AlertController類完成的。因此,AlertController類才是關鍵。
找到AlertController.java文件。打開後不要感到頭暈哦,這個文件中的代碼是很多地。不過這么多代碼對本文的主題也沒什麼用處。下面就找一下控制按鈕的代碼。
在AlertController類的開頭就會看到如下的代碼: View.OnClickListener mButtonHandler = new View.OnClickListener() {
public void onClick(View v) {
Message m = null ;
if (v == mButtonPositive && mButtonPositiveMessage != null ) {
m = Message.obtain(mButtonPositiveMessage);
} else if (v == mButtonNegative && mButtonNegativeMessage != null ) {
m = Message.obtain(mButtonNegativeMessage);
} else if (v == mButtonNeutral && mButtonNeutralMessage != null ) {
m = Message.obtain(mButtonNeutralMessage);
}
if (m != null ) {
m.sendToTarget();
} // Post a message so we dismiss after the above handlers are executed
mHandler.obtainMessage(ButtonHandler.MSG_DISMISS_DIALOG, mDialogInterface)
.sendToTarget();
}
};
從這段代碼中可以猜出來,前幾行代碼用來觸發對話框中的三個按鈕( Positive 、 Negative 和 Neutral )的單擊事件,而最後的代碼則用來關閉對話框(因為我們發現了 MSG_DISMISS_DIALOG 、猜出來的)。
mHandler.obtainMessage(ButtonHandler.MSG_DISMISS_DIALOG, mDialogInterface)
.sendToTarget(); 上面的代碼並不是直接來關閉對話框的,而是通過一個 Handler 來處理,代碼如下:
private static final class ButtonHandler extends Handler {
// Button clicks have Message.what as the BUTTON{1,2,3} constant
private static final int MSG_DISMISS_DIALOG = 1 ;
private WeakReference < DialogInterface > mDialog; public ButtonHandler(DialogInterface dialog) {
mDialog = new WeakReference < DialogInterface > (dialog);
} @Override
public void handleMessage(Message msg) {
switch (msg.what) {
case DialogInterface.BUTTON_POSITIVE:
case DialogInterface.BUTTON_NEGATIVE:
case DialogInterface.BUTTON_NEUTRAL:
((DialogInterface.OnClickListener) msg.obj).onClick(mDialog.get(), msg.what);
break ;
case MSG_DISMISS_DIALOG:
((DialogInterface) msg.obj).dismiss();
}
}
}
從上面代碼的最後可以找到 ((DialogInterface) msg.obj).dismiss();。現在看了這么多源代碼,我們來總結一下對話框按鈕單擊事件的處理過程。在AlertController處理對話框按鈕時會為每一個按鈕添加一個onclick事件。而這個事件類的對象實例就是上面的mButtonHandler。在這個單擊事件中首先會通過發送消息的方式調用為按鈕設置的單擊事件(也就是通過setPositiveButton等方法的第二個參數設置的單擊事件),在觸發完按鈕的單擊事件後,會通過發送消息的方式調用dismiss方法來關閉對話框。而在AlertController類中定義了一個全局的mHandler變數。在AlertController類中通過ButtonHandler類來對象來為mHandler賦值。因此,我們只要使用我們自己Handler對象替換ButtonHandler就可以阻止調用dismiss方法來關閉對話框。下面先在自己的程序中建立一個新的ButtonHandler類(也可叫其他的名)。
class ButtonHandler extends Handler
{ private WeakReference < DialogInterface > mDialog; public ButtonHandler(DialogInterface dialog)
{
mDialog = new WeakReference < DialogInterface > (dialog);
} @Override
public void handleMessage(Message msg)
{
switch (msg.what)
{ case DialogInterface.BUTTON_POSITIVE:
case DialogInterface.BUTTON_NEGATIVE:
case DialogInterface.BUTTON_NEUTRAL:
((DialogInterface.OnClickListener) msg.obj).onClick(mDialog
.get(), msg.what);
break ;
}
}
} 我們可以看到,上面的類和AlertController中的ButtonHandler類很像,只是支掉了switch語句的最後一個case子句(用於調用dismiss方法)和相關的代碼。
下面我們就要為AlertController中的mHandler重新賦值。由於mHandler是private變數,因此,在這里需要使用Java的反射技術來為mHandler賦值。由於在AlertDialog類中的mAlert變數同樣也是private,因此,也需要使用同樣的反射技術來獲得mAlert變數。代碼如下:
先建立一個 AlertDialog 對象
AlertDialog alertDialog = new AlertDialog.Builder( this )
.setTitle( " abc " )
.setMessage( " content " )
.setIcon(R.drawable.icon)
.setPositiveButton( 「確定」,
new OnClickListener()
{
@Override
public void onClick(DialogInterface dialog,
int which)
{ }
}).setNegativeButton( " 取消 " , new OnClickListener()
{ @Override
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
}
}).create()
上面的對話框很普通,單擊哪個按鈕都會關閉對話框。下面在調用 show 方法之前來修改一個 mHandler 變數的值, OK ,下面我們就來見證奇跡的時刻。 try
{
Field field = alertDialog1.getClass().getDeclaredField( " mAlert " );
field.setAccessible( true );
// 獲得mAlert變數的值
Object obj = field.get(alertDialog1);
field = obj.getClass().getDeclaredField( " mHandler " );
field.setAccessible( true );
// 修改mHandler變數的值,使用新的ButtonHandler類
field.set(obj, new ButtonHandler(alertDialog1));
}
catch (Exception e)
{
}
// 顯示對話框
alertDialog.show(); 我們發現,如果加上try catch語句,單擊對話框中的確定按鈕不會關閉對話框(除非在代碼中調用dismiss方法),單擊取消按鈕則會關閉對話框(因為調用了dismiss方法)。如果去了try…catch代碼段,對話框又會恢復正常了。
雖然上面的代碼已經解決了問題,但需要編寫的代碼仍然比較多,為此,我們也可採用另外一種方法來阻止關閉對話框。這種方法不需要定義任何的類。
這種方法需要用點技巧。由於系統通過調用dismiss來關閉對話框,那麼我們可以在dismiss方法上做點文章。在系統調用dismiss方法時會首先判斷對話框是否已經關閉,如果對話框已經關閉了,就會退出dismiss方法而不再繼續關閉對話框了。因此,我們可以欺騙一下系統,當調用dismiss方法時我們可以讓系統以為對話框已經關閉(雖然對話框還沒有關閉),這樣dismiss方法就失效了,這樣即使系統調用了dismiss方法也無法關閉對話框了。
下面讓我們回到AlertDialog的源代碼中,再繼續跟蹤到AlertDialog的父類Dialog的源代碼中。找到dismissDialog方法。實際上,dismiss方法是通過dismissDialog方法來關閉對話框的,dismissDialog方法的代碼如下: private void dismissDialog() {
if (mDecor == null ) {
if (Config.LOGV) Log.v(LOG_TAG,
" [Dialog] dismiss: already dismissed, ignore " );
return ;
}
if ( ! mShowing) {
if (Config.LOGV) Log.v(LOG_TAG,
" [Dialog] dismiss: not showing, ignore " );
return ;
} mWindowManager.removeView(mDecor); mDecor = null ;
mWindow.closeAllPanels();
onStop();
mShowing = false ;
sendDismissMessage();
}
該方法後面的代碼不用管它,先看 if(!mShowing){ … } 這段代碼。這個 mShowing 變數就是判斷對話框是否已關閉的。因此,我們在代碼中通過設置這個變數就可以使系統認為對話框已經關閉,就不再繼續關閉對話框了。由於 mShowing 也是 private 變數,因此,也需要反射技術來設置這個變數。我們可以在對話框按鈕的單擊事件中設置 mShowing ,代碼如下:
try
{
Field field = dialog.getClass()
.getSuperclass().getDeclaredField(
" mShowing " );
field.setAccessible( true );
// 將mShowing變數設為false,表示對話框已關閉
field.set(dialog, false );
dialog.dismiss();}
catch (Exception e)
{}
將上面的代碼加到哪個按鈕的單擊事件代碼中,哪個按鈕就再也無法關閉對話框了。如果要關閉對話框,只需再將 mShowing 設為 true 即可。要注意的是,在一個按鈕里設置了 mShowing 變數,也會影響另一個按鈕的關閉對話框功能,因此,需要在每一個按鈕的單擊事件里都設置 mShowing 變數的值。 本文來自CSDN博客,轉載請標明出處: http://blog.csdn.net/nokiaguy/archive/2010/07/27/5770263.aspx
❹ 濡備綍鍦ˋndroid涓嬩嬌鐢˙inder
瀹炵幇涓涓猙inder閫氫俊瀹炰緥錛岄渶瑕佺粡榪囦互涓嬫ラわ細
錛1錛夎幏寰桽erviceManager鐨勫硅薄寮曠敤
錛2錛夊悜ServiceManager娉ㄥ唽鏂扮殑Service
錛3錛夊湪Client涓閫氳繃ServiceManager鑾峰緱Service瀵硅薄寮曠敤
錛3錛夊湪Client涓鍙戦佽鋒眰錛岀敱Service榪斿洖緇撴灉銆
涓嬮潰鐪嬪叿浣撶殑浠g爜濡備綍瀹炵幇銆
3.1 libmyservice浠g爜瀹炵幇
錛1錛夋柊寤虹洰褰昮rameworks/base/myservice/libservice錛岃繘鍏ヨョ洰褰
view plain
$ cd frameworks/base
$ mkdir myservice
$ cd myservice
$ mkdir libmyservice
$ cd libmyservice
錛2錛夌紪鍐檒ibmyservice/myservic.h鏂囦歡
view plain
#include <utils/threads.h>
#include <utils/RefBase.h>
#include <binder/IInterface.h>
#include <binder/BpBinder.h>
#include <binder/Parcel.h>
namespace android {
class MyService : public BBinder
{
mutable Mutex mLock;
int32_t mNextConnId;
public:
static int instantiate();
MyService();
virtual ~MyService();
virtual status_t onTransact(uint32_t, const Parcel&, Parcel*, uint32_t);
};
}; //namespace
(2)緙栧啓libservice/myservice.cpp鏂囦歡
view plain
#include "myservice.h"
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
namespace android {
static struct sigaction oldact;
static pthread_key_t sigbuskey;
int MyService::instantiate()
{
LOGE("MyService instantiate");
// defaultServiceManager ()鑾峰緱ServiceManager鐨勫硅薄寮曠敤錛宎ddService()鍙鍚慡erviceManager娉ㄥ唽鏂扮殑鏈嶅姟
int r = defaultServiceManager()->addService(String16("android.myservice"), new MyService());
LOGE("MyService r = %d/n", r);
return r;
}
MyService::MyService()
{
LOGV("MyService created");
mNextConnId = 1;
pthread_key_create(&sigbuskey, NULL);
}
MyService::~MyService()
{
pthread_key_delete(sigbuskey);
LOGV("MyService destroyed");
}
// 姣忎釜緋葷粺鏈嶅姟閮界戶鎵胯嚜BBinder綾伙紝閮藉簲閲嶅啓BBinder鐨刼nTransact鉶氬嚱鏁般傚綋鐢ㄦ埛鍙戦佽鋒眰鍒拌揪Service鏃訛紝緋葷粺妗嗘灦浼氳皟鐢⊿ervice鐨刼nTransact鍑芥暟錛岃ュ嚱鏁板垎鏋愭帴鏀跺埌鐨勬暟鎹鍖咃紝璋冪敤鐩稿簲鐨勬帴鍙e嚱鏁板勭悊璇鋒眰
status_t MyService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code)
{
case 0: {
pid_t pid = data.readInt32();
int num = data.readInt32();
num = num + 100;
reply->writeInt32(num);
return NO_ERROR;
}
break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
}; //namespace
錛3錛夌紪鍐檒ibservice/Android.mk鏂囦歡
view plain
# File: Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := myservice.cpp
LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
LOCAL_SHARED_LIBRARIES := libutils libbinder
LOCAL_MODULE_TAGS := optional
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE := libmyservice
include $(BUILD_SHARED_LIBRARY)
錛4錛夌紪璇憀ibmyservice.so鍔ㄦ佸簱
鍦╝ndroid婧愮爜涓葷洰褰曚笅
view plain
$ source build/envsetup.sh
including device/htc/passion/vendorsetup.sh
including device/samsung/crespo4g/vendorsetup.sh
including device/samsung/crespo/vendorsetup.sh
$ mmm frameworks/base/myservice/libmyservice/
緙栬瘧鎴愬姛鍚庣敓鎴愭枃浠訛細out/target/proct/generic/system/lib/libmyservice.so
❺ 如何hook android sdk
1.1 概述
Xposed 是 GitHUB 上 rovo89 大大設計的一個針對 Android 平台的動態劫持項目,通過替換 /system/bin/app_process 程序控制 zygote 進程,使得 app_process 在啟動過程中會載入 XposedBridge.jar 這個 jar 包,從而完成對系統應用的劫持。
Xposed 框架的基本運行環境如下:
因為 Xposed 工作原理是在 /system/bin 目錄下替換文件,在 install 的時候需要root 許可權,但是運行時不需要 root 許可權。
需要在 Android 4.0 以上版本的機器中
2. GitHub 上的 Xposed 資源梳理一下,可以這么分類:
XposedBridge.jar : XposedBridge.jar 是 Xposed 提供的 jar 文件,負責在 Native層與 FrameWork 層進行交互。 /system/bin/app_process 進程啟動過程中會載入該jar 包,其它的 Moles 的開發與運行都是基於該 jar 包的。
Xposed : Xposed 的 C++ 部分,主要是用來替換 /system/bin/app_process ,並為 XposedBridge 提供 JNI 方法。
XposedInstaller : Xposed 的安裝包,負責配置 Xposed 工作的環境並且提供對基於 Xposed 框架的 Moles 的管理。
XposedMods :使用 Xposed 開發的一些 Moles ,其中 AppSettings 是一個可以進行許可權動態管理的應用
1.2 Mechanism :原理
1.2.1 Zygote
在 Android 系統中,應用程序進程都是由 Zygote 進程孵化出來的,而 Zygote 進程是由 Init 進程啟動的。 Zygote 進程在啟動時會創建一個 Dalvik 虛擬機實例,每當它孵化一個新的應用程序進程時,都會將這個 Dalvik 虛擬機實例復制到新的應用程序進程裡面去,從而使得每一個應用程序進程都有一個獨立的 Dalvik 虛擬機實例。
Zygote 進程在啟動的過程中,除了會創建一個 Dalvik 虛擬機實例之外,還會將 Java運行時庫載入到進程中來,以及注冊一些 Android 核心類的 JNI 方法來前面創建的 Dalvik 虛擬機實例中去。注意,一個應用程序進程被 Zygote 進程孵化出來的時候,不僅會獲得 Zygote 進程中的 Dalvik 虛擬機實例拷貝,還會與 Zygote 一起共享 Java 運行時庫。這也就是可以將XposedBridge 這個 jar 包載入到每一個 Android 應用程序中的原因。 XposedBridge 有一個私有的 Native ( JNI )方法 hookMethodNative,這個方法也在 app_process 中使用。這個函數提供一個方法對象利用 Java 的 Reflection 機制來對內置方法覆寫。具體的實現可以看下文的 Xposed 源代碼分析。
1.2.2 Hook/Replace
Xposed 框架中真正起作用的是對方法的 hook 。在 Repackage 技術中,如果要對APK 做修改,則需要修改 Smali 代碼中的指令。而另一種動態修改指令的技術需要在程序運行時基於匹配搜索來替換 smali 代碼,但因為方法聲明的多樣性與復雜性,這種方法也比較復雜。
在 Android 系統啟動的時候, zygote 進程載入 XposedBridge 將所有需要替換的 Method 通過 JNI 方法 hookMethodNative 指向 Native 方法 xposedCallHandler , xposedCallHandler 在轉入 handleHookedMethod 這個 Java 方法執行用戶規定的 Hook Func 。
XposedBridge 這個 jar 包含有一個私有的本地方法: hookMethodNative ,該方法在附加的 app_process 程序中也得到了實現。它將一個方法對象作為輸入參數(你可以使用 Java 的反射機制來獲取這個方法)並且改變 Dalvik 虛擬機中對於該方法的定義。它將該方法的類型改變為 native 並且將這個方法的實現鏈接到它的本地的通用類的方法。換言之,當調用那個被 hook 的方法時候,通用的類方法會被調用而不會對調用者有任何的影響。在 hookMethodNative 的實現中,會調用 XposedBridge中的handleHookedMethod這個方法來傳遞參數。 handleHookedMethod 這個方法類似於一個統一調度的 Dispatch 常式,其對應的底層的 C++ 函數是 xposedCallHandler 。而 handleHookedMethod 實現裡面會根據一個全局結構 hookedMethodCallbacks 來選擇相應的 hook函數,並調用他們的 before, after 函數。
當多模塊同時 Hook 一個方法的時候, Xposed 會自動根據 Mole 的優先順序來排序,調用順序如下:
A.before -> B.before -> original method -> B.after -> A.after
2 源代碼分析
2.1 Cpp 模塊
其文件分類如下:
app_main.cpp :類似 AOSP 中的 frameworks/base/cmds/app_process/app_main.cpp,即/system/bin/app_process 這個 zygote 真實身份的應用程序的源代碼。關於zygote 進程的分析可以參照 Android:AOSP&Core 中的 Zygote 進程詳解。
xposed.cpp :提供給 app_main.cpp 的調用函數以及 XposedBridge 的 JNI 方法的實現。主要完成初始化工作以及 Framework 層的 Method 的 Hook 操作。
xposed.h , xposed_offsets.h :頭文件
Xposed 框架中的 app_main.cpp 相對於 AOSP 的 app_main.cpp 中修改之處主要為區分了調用 runtime.start() 函數的邏輯。 Xposed 框架中的 app_main.cpp 在此處會根據情況選擇是載入 XposedBridge 類還是 ZygoteInit 或者 RuntimeInit 類。而實際的載入 XposedBridge 以及注冊 JNI 方法的操作發生在第四步: xposedOnVmCreated中。
1.包含 cutils/properties.h ,主要用於獲取、設置環境變數, xposed.cpp 中需要將XposedBridge 設置到 ClassPath 中。
2.包含了 dlfcn.h ,用於對動態鏈接庫的操作。
3.包含了 xposed.h ,需要調用 xposed.cpp 中的函數,譬如在虛擬機創建時注冊 JNI 函數。
4.增加了 initTypePointers 函數,對於 Android SDK 大於等於 18 的會獲取到 atrace_set_tracing_enabled 函數指針,在 Zygote 啟動時調用。
5.AppRuntime 類中的 onVmCreated 函數中增加 xposedOnVmCreated 函數調用。
6.源代碼中的 Log* 全部重命名為 ALog*, 所以 Logv 替換為 Alogv ,但是功能不變。
7.Main 函數開始處增加了大量的代碼,但是對於 SDK 版本小於 16 的可以不用考慮。
2.1.1 Main 函數: zygote 入口
int main(int argc, char* const argv[])
❻ Java程序 android log.v能列印十六進制的數據
Log.e("string","answer"+Integer.toHexString(your num.));
❼ android中camera的hal模塊怎麼被調用
CameraService.cpp (frameworks\base\services\camera\libcameraservice)
中調用hw_get_mole
[cpp] view plain print?
void CameraService::onFirstRef()
{
BnCameraService::onFirstRef();
<span style="color: rgb(255, 0, 0);">if (hw_get_mole(CAMERA_HARDWARE_MODULE_ID,
(const hw_mole_t **)&mMole) < 0)</span> {
LOGE("Could not load camera HAL mole");
mNumberOfCameras = 0;
}
else {
mNumberOfCameras = mMole->get_number_of_cameras();
if (mNumberOfCameras > MAX_CAMERAS) {
LOGE("Number of cameras(%d) > MAX_CAMERAS(%d).",
mNumberOfCameras, MAX_CAMERAS);
mNumberOfCameras = MAX_CAMERAS;
}
for (int i = 0; i < mNumberOfCameras; i++) {
setCameraFree(i);
}
}
}
void CameraService::onFirstRef()
{
BnCameraService::onFirstRef();
if (hw_get_mole(CAMERA_HARDWARE_MODULE_ID,
(const hw_mole_t **)&mMole) < 0) {
LOGE("Could not load camera HAL mole");
mNumberOfCameras = 0;
}
else {
mNumberOfCameras = mMole->get_number_of_cameras();
if (mNumberOfCameras > MAX_CAMERAS) {
LOGE("Number of cameras(%d) > MAX_CAMERAS(%d).",
mNumberOfCameras, MAX_CAMERAS);
mNumberOfCameras = MAX_CAMERAS;
}
for (int i = 0; i < mNumberOfCameras; i++) {
setCameraFree(i);
}
}
}
看一下hw_get_mole是怎麼回事
[cpp] view plain print?
int hw_get_mole(const char *id, const struct hw_mole_t **mole)
{
return <span style="color: rgb(255, 0, 0);">hw_get_mole_by_class(id, NULL, mole);
</span>}
int hw_get_mole(const char *id, const struct hw_mole_t **mole)
{
return hw_get_mole_by_class(id, NULL, mole);
}
他只是一個封裝實際調用了[email protected] (hardware\libhardware)
好在不長,看看吧
[cpp] view plain print?
int hw_get_mole_by_class(const char *class_id, const char *inst,
const struct hw_mole_t **mole)
{
int status;
int i;
const struct hw_mole_t *hmi = NULL;
<span style="color: rgb(255, 0, 0);"> char prop[PATH_MAX];//幾個關鍵的數組
char path[PATH_MAX];//在下面起了重要
char name[PATH_MAX];//作用
</span>
if (inst)
snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
else
strlcpy(name, class_id, PATH_MAX);//走這里
/*
* Here we rely on the fact that calling dlopen multiple times on
* the same .so will simply increment a refcount (and not load
* a new of the library).
* We also assume that dlopen() is thread-safe.
*/
/* Loop through the configuration variants looking for a mole */
for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {
if (i < HAL_VARIANT_KEYS_COUNT) {
if (property_get(variant_keys[i], prop, NULL) == 0)
<span style="color: rgb(255, 0, 0);">//在這里將prop的路徑得到,分別從
"ro.hardware[qcom]"
"ro.proct.board"[7x27],
"ro.board.platform"[msm7627a],
"ro.arch",
"ro.hw_platform"[QRD_SKU3-1100]
這幾個屬性文件中獲得硬體的信息
有些硬體信息的字元串會出現在編譯後生成的.so名字中</span>
{
continue;
}
snprintf(path, sizeof(path), "%s/%s.%s.so",
HAL_LIBRARY_PATH2, name, prop);
if (access(path, R_OK) == 0) break;
snprintf(path, sizeof(path), "%s/%s.%s.so",
HAL_LIBRARY_PATH1, name, prop);<span style="color: rgb(255, 0, 0);">//走這里,在這里得到/system/lib/hw/camera.msm7627a.so
這樣一個路徑,這個庫里有QualcommCamera.cpp,這是
camera模塊HAL代碼開始的地方</span>
if (access(path, R_OK) == 0) break;
} else {
snprintf(path, sizeof(path), "%s/%s.default.so",
HAL_LIBRARY_PATH1, name);
if (access(path, R_OK) == 0) break;
}
}
status = -ENOENT;
if (i < HAL_VARIANT_KEYS_COUNT+1) {
/* load the mole, if this fails, we're doomed, and we should not try
* to load a different variant. */
status = load(class_id, path, mole);<span style="color: rgb(255, 0, 0);">//這里關鍵,函數的三個參數可以串聯成一句話:
到path(/system/lib/hw/camera.msm7627a.so)這個路徑下找到一個id(camera)匹配的mole</span>
}
return status;
}
int hw_get_mole_by_class(const char *class_id, const char *inst,
const struct hw_mole_t **mole)
{
int status;
int i;
const struct hw_mole_t *hmi = NULL;
char prop[PATH_MAX];//幾個關鍵的數組
char path[PATH_MAX];//在下面起了重要
char name[PATH_MAX];//作用
if (inst)
snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
else
strlcpy(name, class_id, PATH_MAX);//走這里
/*
* Here we rely on the fact that calling dlopen multiple times on
* the same .so will simply increment a refcount (and not load
* a new of the library).
* We also assume that dlopen() is thread-safe.
*/
/* Loop through the configuration variants looking for a mole */
for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {
if (i < HAL_VARIANT_KEYS_COUNT) {
if (property_get(variant_keys[i], prop, NULL) == 0)
//在這里將prop的路徑得到,分別從
"ro.hardware[qcom]"
"ro.proct.board"[7x27],
"ro.board.platform"[msm7627a],
"ro.arch",
"ro.hw_platform"[QRD_SKU3-1100]
這幾個屬性文件中獲得硬體的信息
有些硬體信息的字元串會出現在編譯後生成的.so名字中
{
continue;
}
snprintf(path, sizeof(path), "%s/%s.%s.so",
HAL_LIBRARY_PATH2, name, prop);
if (access(path, R_OK) == 0) break;
snprintf(path, sizeof(path), "%s/%s.%s.so",
HAL_LIBRARY_PATH1, name, prop);//走這里,在這里得到/system/lib/hw/camera.msm7627a.so
這樣一個路徑,這個庫里有QualcommCamera.cpp,這是
camera模塊HAL代碼開始的地方
if (access(path, R_OK) == 0) break;
} else {
snprintf(path, sizeof(path), "%s/%s.default.so",
HAL_LIBRARY_PATH1, name);
if (access(path, R_OK) == 0) break;
}
}
status = -ENOENT;
if (i < HAL_VARIANT_KEYS_COUNT+1) {
/* load the mole, if this fails, we're doomed, and we should not try
* to load a different variant. */
status = load(class_id, path, mole);//這里關鍵,函數的三個參數可以串聯成一句話:
到path(/system/lib/hw/camera.msm7627a.so)這個路徑下找到一個id(camera)匹配的mole
}
return status;
}
再來看看load這個函數@hardware.c (hardware\libhardware)
[cpp] view plain print?
static int load(const char *id,
const char *path,
const struct hw_mole_t **pHmi)
{
int status;
void *handle;
struct hw_mole_t *hmi;
/*
* load the symbols resolving undefined symbols before
* dlopen returns. Since RTLD_GLOBAL is not or'd in with
* RTLD_NOW the external symbols will not be global
*/
handle = dlopen(path, RTLD_NOW);
if (handle == NULL) {
char const *err_str = dlerror();
LOGE("load: mole=%s\n%s", path, err_str?err_str:"unknown");
status = -EINVAL;
goto done;
}
<span style="color: rgb(255, 0, 0);"> /* Get the address of the struct hal_mole_info. */
const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
hmi = (struct hw_mole_t *)dlsym(handle, sym);
</span> if (hmi == NULL) {
LOGE("load: couldn't find symbol %s", sym);
status = -EINVAL;
goto done;
}
/* Check that the id matches */
if (strcmp(id, hmi->id) != 0) {
LOGE("load: id=%s != hmi->id=%s", id, hmi->id);
status = -EINVAL;
goto done;
}
hmi->dso = handle;
/* success */
status = 0;
done:
if (status != 0) {
hmi = NULL;
if (handle != NULL) {
dlclose(handle);
handle = NULL;
}
} else {
LOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
id, path, *pHmi, handle);
}
*pHmi = hmi;
return status;
}
static int load(const char *id,
const char *path,
const struct hw_mole_t **pHmi)
{
int status;
void *handle;
struct hw_mole_t *hmi;
/*
* load the symbols resolving undefined symbols before
* dlopen returns. Since RTLD_GLOBAL is not or'd in with
* RTLD_NOW the external symbols will not be global
*/
handle = dlopen(path, RTLD_NOW);
if (handle == NULL) {
char const *err_str = dlerror();
LOGE("load: mole=%s\n%s", path, err_str?err_str:"unknown");
status = -EINVAL;
goto done;
}
/* Get the address of the struct hal_mole_info. */
const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
hmi = (struct hw_mole_t *)dlsym(handle, sym);
if (hmi == NULL) {
LOGE("load: couldn't find symbol %s", sym);
status = -EINVAL;
goto done;
}
/* Check that the id matches */
if (strcmp(id, hmi->id) != 0) {
LOGE("load: id=%s != hmi->id=%s", id, hmi->id);
status = -EINVAL;
goto done;
}
hmi->dso = handle;
/* success */
status = 0;
done:
if (status != 0) {
hmi = NULL;
if (handle != NULL) {
dlclose(handle);
handle = NULL;
}
} else {
LOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
id, path, *pHmi, handle);
}
*pHmi = hmi;
return status;
}
在打開的.so(camera.msm7627a.so)中查找HMI符號的地址,並保存在hmi中。至此,.so中的hw_mole_t已經被成功獲取,從而可以根
據它獲取別的相關介面。
1)HAL通過hw_get_mole函數獲取hw_mole_t
2)HAL通過hw_mole_t->methods->open獲取hw_device_t指針,並在此open函數中初始化hw_device_t的包裝結構中的
函數及hw_device_t中的close函數,如gralloc_device_open。
3)三個重要的數據結構:
a) struct hw_device_t: 表示硬體設備,存儲了各種硬體設備的公共屬性和方法
b)struct hw_mole_t: 可用hw_get_mole進行載入的mole
c)struct hw_mole_methods_t: 用於定義操作設備的方法,其中只定義了一個打開設備的方法open.
❽ 闂棰樻眰鍔╋紝閫氳繃ndk鍦ㄦ湰鍦癈浠g爜涓瀹炵幇Surface鏄劇ず
浣犲彲浠ュ弬鑰僋DK鍖呴噷闈㈢殑sample錛歛ndroid-ndk-r10samples ative-codec銆傞噷闈㈡湁濡備綍浠巎ava灞備紶閫扴urface鍒皀ative鍒頒唬鐮侊細
/**Nativemethods,implementedinjnifolder*/
();
(Stringfilename);
(booleanisPlaying);
();
(Surfacesurface);
();
{
;
GLViewVideoSink(){
mMyGLSurfaceView=myGLSurfaceView;
}
@Override
voidsetFixedSize(intwidth,intheight){
}
@Override
voiseAsSinkForNative(){
SurfaceTexturest=mMyGLSurfaceView.getSurfaceTexture();
Surfaces=newSurface(st);
setSurface(s);
s.release();
}
}
native code錛
//setthesurface
voidJava_com_example_nativecodec_NativeCodec_setSurface(JNIEnv*env,jclassclazz,jobjectsurface)
{
//
if(data.window){
ANativeWindow_release(data.window);
data.window=NULL;
}
data.window=ANativeWindow_fromSurface(env,surface);
LOGV("@@@setsurface%p",data.window);
}