简书链接:android插件化简要概述以及谈谈QQ的插件化原理大概
文章字数:1831,阅读全文大约需要7分钟
构建一个插件化了解几个关键点,就可以实现一个可以架构一个支持比较简单粗糙的插件化apk了,能拿到插件apk classloader的 就可以实现加载任意插件apk的类
知道Resources如何根据apk生成自然就可以操作任意布局,其他的就是使用类似包装模式一样的架构完成生命周期的代理.

动态加载插件apk dex

1
2
3
4
5
        File dexOutFileDir = context.getDir("dex", Context.MODE_PRIVATE);
String pluginApkPath=new File(context.getDir("plugin"),"my.apk").getAbsolutePath();
dexClassLoader = new DexClassLoader(pluginApkPath, dexOutFileDir .getAbsolutePath() , null, context.getClassLoader());
//拿到了classloader就可以加载任意类了

动态加载资源

1
2
3
4
5
6
7
8
9
try {
AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
addAssetPath.invoke(assetManager, path);
resources = new Resources(assetManager, context.getResources().getDisplayMetrics(), context.getResources().getConfiguration());
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(context, "加载失败", Toast.LENGTH_SHORT).show();
}

演示架构代理插件activity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
//主activiy声明,但是不能是单例,这个不需要多说,其次在onCreate里面加载插件apk。
public class MyProxyActivity extends Activity {

private String className;
IPluginProxyActivity ipluginProxyActivity;
// com.dongnao.alvin.taopiaopiao.MainActivity
@Override
public void onCreate(@Nullable Bundle savedInstanceState ) {
super.onCreate(savedInstanceState );
className = getIntent().getStringExtra("className");
// class

try {
Class activityClass = getClassLoader().loadClass(className);
Constructor constructor = activityClass.getConstructor(new Class[]{});
Object instance= constructor.newInstance(new Object[]{});
// 可以
ipluginProxyActivity = (IPluginProxyActivity) instance;

ipluginProxyActivity.attach(this);
Bundle bundle = new Bundle();
ipluginProxyActivity.onCreate(bundle);

} catch (Exception e) {

Toast.makeText(this, "找不到class:"+className, Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}

@Override
public void startActivity(Intent intent) {
String className1=intent.getStringExtra("className");
Intent intent1 = new Intent(this, MyProxyActivity.class);
intent1.putExtra("className", className1);
super.startActivity(intent1);
}

@Override
public ComponentName startService(Intent service) {
String serviceName = service.getStringExtra("serviceName");
Intent intent1 = new Intent(this, ProxyService.class);
intent1.putExtra("serviceName", serviceName);
return super.startService(intent1);
}

@Override
public ClassLoader getClassLoader() {
return PluginManager.getInstance().getDexClassLoader();//可以定义pluginId,方便扩展
}

@Override
public Resources getResources() {
return PluginManager.getInstance().getResources();//可以定义pluginId,方便扩展
}


@Override
protected void onStart() {
super.onStart();
if(ipluginProxyActivity ==null){
Toast.makeText(this, "获取payInterActivity fail", Toast.LENGTH_SHORT).show();
return;
}
ipluginProxyActivity.onStart();
}

@Override
protected void onDestroy() {
super.onDestroy();
ipluginProxyActivity.onDestroy();
}

@Override
protected void onPause() {
super.onPause();
ipluginProxyActivity.onPause();
}
}

根据资源id查资源名或反查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49


public int getIdentifier(int resId) {
if (isDefaultSkin) {
return resId;
}
//在皮肤包中不一定就是 当前程序的 id
//获取对应id 在当前的名称 colorPrimary
//R.drawable.ic_launcher
String resName = mAppResources.getResourceEntryName(resId);//ic_launcher
String resType = mAppResources.getResourceTypeName(resId);//drawable
int skinId = mSkinResources.getIdentifier(resName, resType, mSkinPkgName);
return skinId;
}

public int getColor(int resId) {
if (isDefaultSkin) {
return mAppResources.getColor(resId);
}
int skinId = getIdentifier(resId);
if (skinId == 0) {
return mAppResources.getColor(resId);
}
return mSkinResources.getColor(skinId);
}

public ColorStateList getColorStateList(int resId) {
if (isDefaultSkin) {
return mAppResources.getColorStateList(resId);
}
int skinId = getIdentifier(resId);
if (skinId == 0) {
return mAppResources.getColorStateList(resId);
}
return mSkinResources.getColorStateList(skinId);
}

public Drawable getDrawable(int resId) {
//如果有皮肤 isDefaultSkin false 没有就是true
if (isDefaultSkin) {
return mAppResources.getDrawable(resId);
}
int skinId = getIdentifier(resId);
if (skinId == 0) {
return mAppResources.getDrawable(resId);
}
return mSkinResources.getDrawable(skinId);
}

hook拦截activity启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//可以追踪startAcitivty最终调用哪里
Class<?> activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative");
Field gDefaultField = activityManagerNativeClass.getDeclaredField("gDefault");
gDefaultField.setAccessible(true);
Object gDefault = gDefaultField.get(null);
// gDefault是一个 android.util.Singleton对象; 我们取出这个单例里面的字段
Class<?> singleton = Class.forName("android.util.Singleton");
Field mInstanceField = singleton.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
// ActivityManagerNative 的gDefault对象里面原始的 IActivityManager对象
Object rawIActivityManager = mInstanceField.get(gDefault);
// 创建一个这个对象的代理对象, 然后替换这个字段, 让我们的代理对象帮忙干活
Class<?> iActivityManagerInterface = Class.forName("android.app.IActivityManager")
Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader
new Class<?>[] { iActivityManagerInterface }, new IActivityManagerHandler(
mInstanceField.set(gDefault, proxy);

反射实现静态recervicer变成动态加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//反射的代码 根据 如下2个文件源码分析总结

//\frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java
//\frameworks\base\core\java\android\content\pm\PackageParser.java
try {

Class packageParserClass = Class.forName("android.content.pm.PackageParser");
Method parsePackageMethod = packageParserClass.getDeclaredMethod("parsePackage", File.class, int.class);
Object packageParser = packageParserClass.newInstance();
Object packageObj= parsePackageMethod.invoke(packageParser, new File(path), PackageManager.GET_ACTIVITIES);
Field receiverField=packageObj.getClass().getDeclaredField("receivers");
List receivers = (List) receiverField.get(packageObj);

Class<?> componentClass = Class.forName("android.content.pm.PackageParser$Component");
Field intentsField = componentClass.getDeclaredField("intents");

// 调用generateActivityInfo 方法, 把PackageParser.Activity 转换成
Class<?> packageParser$ActivityClass = Class.forName("android.content.pm.PackageParser$Activity");
// generateActivityInfo方法
Class<?> packageUserStateClass = Class.forName("android.content.pm.PackageUserState");
Object defaltUserState= packageUserStateClass.newInstance();
Method generateReceiverInfo = packageParserClass.getDeclaredMethod("generateActivityInfo",
packageParser$ActivityClass, int.class, packageUserStateClass, int.class);
Class<?> userHandler = Class.forName("android.os.UserHandle");
Method getCallingUserIdMethod = userHandler.getDeclaredMethod("getCallingUserId");
int userId = (int) getCallingUserIdMethod.invoke(null);
for (Object activity : receivers) {
ActivityInfo info= (ActivityInfo) generateReceiverInfo.invoke(packageParser, activity,0, defaltUserState, userId);
BroadcastReceiver broadcastReceiver= (BroadcastReceiver) dexClassLoader.loadClass(info.name).newInstance();
List<? extends IntentFilter> intents= (List<? extends IntentFilter>) intentsField.get(activity);
for (IntentFilter intentFilter : intents) {
context.registerReceiver(broadcastReceiver, intentFilter);
}
}
//generateActivityInfo
} catch (Exception e) {
e.printStackTrace();
}

定义接口实现类加载后直接强转接口

支持拦截的代理接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
//Proxy 只支持接口,因此没有定义接口的没戏
public interface IActivityManager extends IInterface {

public int startActivity(IApplicationThread caller,
Intent intent, String resolvedType, Uri[] grantedUriPermissions,
int grantedMode, IBinder resultTo, String resultWho, int requestCode,
boolean onlyIfNeeded, boolean debug) throws RemoteException;
public WaitResult startActivityAndWait(IApplicationThread caller,
Intent intent, String resolvedType, Uri[] grantedUriPermissions,
int grantedMode, IBinder resultTo, String resultWho, int requestCode,
boolean onlyIfNeeded, boolean debug) throws RemoteException;
public int startActivityWithConfig(IApplicationThread caller,
Intent intent, String resolvedType, Uri[] grantedUriPermissions,
int grantedMode, IBinder resultTo, String resultWho, int requestCode,
boolean onlyIfNeeded, boolean debug, Configuration newConfig) throws RemoteException;
public int startActivityIntentSender(IApplicationThread caller,
IntentSender intent, Intent fillInIntent, String resolvedType,
IBinder resultTo, String resultWho, int requestCode,
int flagsMask, int flagsValues) throws RemoteException;
public boolean startNextMatchingActivity(IBinder callingActivity,
Intent intent) throws RemoteException;
public boolean finishActivity(IBinder token, int code, Intent data)
throws RemoteException;
public void finishSubActivity(IBinder token, String resultWho, int requestCode) throws RemoteException;
public boolean willActivityBeVisible(IBinder token) throws RemoteException;
public Intent registerReceiver(IApplicationThread caller,
IIntentReceiver receiver, IntentFilter filter,
String requiredPermission) throws RemoteException;
public void unregisterReceiver(IIntentReceiver receiver) throws RemoteException;
public static final int BROADCAST_SUCCESS = 0;
public static final int BROADCAST_STICKY_CANT_HAVE_PERMISSION = -1;
public int broadcastIntent(IApplicationThread caller, Intent intent,
String resolvedType, IIntentReceiver resultTo, int resultCode,
String resultData, Bundle map, String requiredPermission,
boolean serialized, boolean sticky) throws RemoteException;
public void unbroadcastIntent(IApplicationThread caller, Intent intent) throws RemoteException;
/* oneway */
public void finishReceiver(IBinder who, int resultCode, String resultData, Bundle map, boolean abortBroadcast) throws RemoteException;
public void attachApplication(IApplicationThread app) throws RemoteException;
/* oneway */
public void activityIdle(IBinder token, Configuration config) throws RemoteException;
public void activityPaused(IBinder token, Bundle state) throws RemoteException;
/* oneway */
public void activityStopped(IBinder token,
Bitmap thumbnail, CharSequence description) throws RemoteException;
/* oneway */
public void activityDestroyed(IBinder token) throws RemoteException;
public String getCallingPackage(IBinder token) throws RemoteException;
public ComponentName getCallingActivity(IBinder token) throws RemoteException;
public List getTasks(int maxNum, int flags,
IThumbnailReceiver receiver) throws RemoteException;
public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum,
int flags) throws RemoteException;
public List getServices(int maxNum, int flags) throws RemoteException;
public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState()
throws RemoteException;
public void moveTaskToFront(int task) throws RemoteException;
public void moveTaskToBack(int task) throws RemoteException;
public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) throws RemoteException;
public void moveTaskBackwards(int task) throws RemoteException;
public int getTaskForActivity(IBinder token, boolean onlyRoot) throws RemoteException;
public void finishOtherInstances(IBinder token, ComponentName className) throws RemoteException;
/* oneway */
public void reportThumbnail(IBinder token,
Bitmap thumbnail, CharSequence description) throws RemoteException;
public ContentProviderHolder getContentProvider(IApplicationThread caller,
String name) throws RemoteException;
public void removeContentProvider(IApplicationThread caller,
String name) throws RemoteException;
public void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder> providers) throws RemoteException;
public PendingIntent getRunningServiceControlPanel(ComponentName service)
throws RemoteException;
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType) throws RemoteException;
public int stopService(IApplicationThread caller, Intent service,
String resolvedType) throws RemoteException;
public boolean stopServiceToken(ComponentName className, IBinder token,
int startId) throws RemoteException;
public void setServiceForeground(ComponentName className, IBinder token,
int id, Notification notification, boolean keepNotification) throws RemoteException;
public int bindService(IApplicationThread caller, IBinder token,
Intent service, String resolvedType,
IServiceConnection connection, int flags) throws RemoteException;
public boolean unbindService(IServiceConnection connection) throws RemoteException;
public void publishService(IBinder token,
Intent intent, IBinder service) throws RemoteException;
public void unbindFinished(IBinder token, Intent service,
boolean doRebind) throws RemoteException;
/* oneway */
public void serviceDoneExecuting(IBinder token, int type, int startId,
int res) throws RemoteException;
public IBinder peekService(Intent service, String resolvedType) throws RemoteException;

public boolean bindBackupAgent(ApplicationInfo appInfo, int backupRestoreMode)
throws RemoteException;
public void backupAgentCreated(String packageName, IBinder agent) throws RemoteException;
public void unbindBackupAgent(ApplicationInfo appInfo) throws RemoteException;
public void killApplicationProcess(String processName, int uid) throws RemoteException;

public boolean startInstrumentation(ComponentName className, String profileFile,
int flags, Bundle arguments, IInstrumentationWatcher watcher)
throws RemoteException;
public void finishInstrumentation(IApplicationThread target,
int resultCode, Bundle results) throws RemoteException;

public Configuration getConfiguration() throws RemoteException;
public void updateConfiguration(Configuration values) throws RemoteException;
public void setRequestedOrientation(IBinder token,
int requestedOrientation) throws RemoteException;
public int getRequestedOrientation(IBinder token) throws RemoteException;

public ComponentName getActivityClassForToken(IBinder token) throws RemoteException;
public String getPackageForToken(IBinder token) throws RemoteException;

public static final int INTENT_SENDER_BROADCAST = 1;
public static final int INTENT_SENDER_ACTIVITY = 2;
public static final int INTENT_SENDER_ACTIVITY_RESULT = 3;
public static final int INTENT_SENDER_SERVICE = 4;
public IIntentSender getIntentSender(int type,
String packageName, IBinder token, String resultWho,
int requestCode, Intent intent, String resolvedType, int flags) throws RemoteException;
public void cancelIntentSender(IIntentSender sender) throws RemoteException;
public boolean clearApplicationUserData(final String packageName,
final IPackageDataObserver observer) throws RemoteException;
public String getPackageForIntentSender(IIntentSender sender) throws RemoteException;

public void setProcessLimit(int max) throws RemoteException;
public int getProcessLimit() throws RemoteException;

public void setProcessForeground(IBinder token, int pid, boolean isForeground) throws RemoteException;

public int checkPermission(String permission, int pid, int uid)
throws RemoteException;

public int checkUriPermission(Uri uri, int pid, int uid, int mode)
throws RemoteException;
public void grantUriPermission(IApplicationThread caller, String targetPkg,
Uri uri, int mode) throws RemoteException;
public void revokeUriPermission(IApplicationThread caller, Uri uri,
int mode) throws RemoteException;

public void showWaitingForDebugger(IApplicationThread who, boolean waiting)
throws RemoteException;

public void getMemoryInfo(ActivityManager.MemoryInfo outInfo) throws RemoteException;

public void killBackgroundProcesses(final String packageName) throws RemoteException;
public void forceStopPackage(final String packageName) throws RemoteException;

// Note: probably don't want to allow applications access to these.
public void goingToSleep() throws RemoteException;
public void wakingUp() throws RemoteException;

public void unhandledBack() throws RemoteException;
public ParcelFileDescriptor openContentUri(Uri uri) throws RemoteException;
public void setDebugApp(
String packageName, boolean waitForDebugger, boolean persistent)
throws RemoteException;
public void setAlwaysFinish(boolean enabled) throws RemoteException;
public void setActivityController(IActivityController watcher)
throws RemoteException;

public void enterSafeMode() throws RemoteException;

public void noteWakeupAlarm(IIntentSender sender) throws RemoteException;

public boolean killPids(int[] pids, String reason) throws RemoteException;
//省略了很多............................

我利用这个技术做了什么?

我的机器人插件功能就是这样实现的,各位想开发QQ机器人插件的朋友欢迎加入哦!
插件sdk支持禁言,踢人

插件demo地址
https://gitee.com/51bwn/RobotPluginSDK
插件机器人没有ui界面哈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
DexClassLoader dexClassLoader = new DexClassLoader(currentApk.getAbsolutePath(), getDefaultPluginDexPath(context).getAbsolutePath(), null, context.getClassLoader());
try {
Class<?> aClass = dexClassLoader.loadClass(Cns.PLUGIN_MAIN_ENTRY_FILE);
PluginInterface pluginInterface = (PluginInterface) aClass.newInstance();
pluginInterface.onCreate(context);
QueryPluginModel model = new QueryPluginModel();
model.setPluginInterface(pluginInterface);
model.setOfficial(pluginInterface.getPackageName().startsWith(BuildConfig.APPLICATION_ID));
model.setPath(currentApk.getAbsolutePath());
if (onCreateNotify != null) {
onCreateNotify.onEach(pluginInterface);
}
list.add(model);
if (BuildConfig.DEBUG) {

Log.w(TAG, "加载插件" + currentApk.getAbsolutePath() + "成功");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
Log.e(TAG, "加载" + currentApk.getName() + "文件插件失败,非机器人插件或插件apk被混淆,导致找不到" + Cns.PLUGIN_MAIN_ENTRY_FILE + "类");
} catch (IllegalAccessException e) {
e.printStackTrace();
Log.e(TAG, "加载插件失败,无法创建对象 IllegalAccessException 可能没有访问权限");
} catch (InstantiationException e) {
e.printStackTrace();
Log.e(TAG, "加载插件失败,无法创建对象InstantiationException ");
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "加载插件失败,未知异常 " + e.getMessage());
} catch (Error e) {
e.printStackTrace();
Log.e(TAG, "加载插件失败,未知错误 " + e.getMessage());
}

上面的方式是固定的class规定,实际上也可以读取资产目录,然后只读取资产目录文件里面规定的类型,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
//PluginInterface 文件
/**
* Created by qssq on 2018/1/21 [email protected]
*/

public interface PluginInterface {
/**
* 插件作者
* @return
*/
public String getAuthorName();
/**
* 当机器人插件被创建加载后,会回调配置api控制类给插件,插件可以自己存储为成员变量,在适当的逻辑中进行操作,此api接口回调支持踢人禁言,发消息
* @param instance
*/
public void onReceiveControlApi(PluginCtronolInterface instance);

/**
* 插件的版本号
*
* @return
*/
public int getVersionCode();
public String getBuildTime();
/**
*
* @return 插件的版本名
*/
public String getVersionName();

/**
*
* @return 插件的包名(不要和作者的包名一样!否则)为了用户的安全,恶意流氓插件我会强制处理,对于冒充官方包名的插件校验签名。所以不要用cn.
* qssq666开头的包名。
*/
public String getPackageName();

/**
* 此机器人插件的描述信息
*
* @return 返回此插件的描述信息
*/
public String getDescript();
/**
* 插件的名字,这里当然是中文名了,给用户看的名字嘛
* @return
*/
public String getPluginName();

// boolean isOfficial();
/**
* 本插件是否被禁用了,这和卸载不同,卸载的话文件都删除了,但是是否禁用是由用户自己实现的
*
* @return
*/
boolean isDisable();

/**
* ,在修改机器人插件配置右边的选项卡会调用setDisable
* 不写无法禁用,影响体验,导致的后果就是被用户卸载咯!具体怎么实现禁用请参考demo源码
*
* @param disable
*/
void setDisable(boolean disable);

/**
* 插件被加载进来后会把宿主的上下文传递过来,不过我感觉有风险啊,有了这东西什么都可以反射了。
*
* @param context
*/
void onCreate(Context context);

/**
* 这里是处理逻辑的关键,首先自身命令和管理员命令逻辑会先走,之后走这里提供消息判断是否处理了此消息,如果处理了就返回true,
*
* @param item
* @return 插件处理了本消息就返回true,那么其他插件,或者机器人将不会被处理,防止冲突。
*/
boolean onReceiveMsgIsNeedIntercept(MsgItem item);

/**
* 这个方法暂时没用,
*/
void onDestory();

/**
* 插件刚加载后会提供一个机器人配置查询接口,比如已经禁用了群聊功能了,你这个机器人插件不听话还进行处理,那么下场也是被卸载了.
*
* @param robotConfigInterface
*/
void onReceiveRobotConfig(RobotConfigInterface robotConfigInterface);

}

分析手机QQ插件化原理

1
//com.tencent.mobileqq.pluginsdk.PluginProxyActivity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
private void b() throws Exception {
PackageInfo packageInfoWithException;
PackageInfo packageInfo = (PackageInfo) PluginStatic.d.get(this.g);
if (packageInfo == null) {
if (DebugHelper.sDebug) {
DebugHelper.log("PluginProxyActivity.initPlugin start getPackageInfo");
}
try {
packageInfoWithException = ApkFileParser.getPackageInfoWithException(this, this.g, 129);
if (DebugHelper.sDebug) {
DebugHelper.log("PluginProxyActivity.initPlugin end getPackageInfo, " + packageInfoWithException);
}
if (packageInfoWithException == null) {
throw new a("Get Package Info Failed!");
}
PluginStatic.d.put(this.g, packageInfoWithException);
} catch (Throwable th) {
a aVar = new a("getPackageInfoWithException", th);
}
} else {
packageInfoWithException = packageInfo;
}
if (this.j == null || this.j.length() == 0) {
if (packageInfoWithException.activities == null || packageInfoWithException.activities.length == 0) {
throw new b();
}
this.j = packageInfoWithException.activities[0].name;
}
ClassLoader a = PluginStatic.a((Context) this, this.i, this.g);
getIntent().setExtrasClassLoader(a);
if (DebugHelper.sDebug) {
DebugHelper.log("PluginProxyActivity.initPlugin start loadClass");
}
this.d = a.loadClass(this.j);
if (DebugHelper.sDebug) {
DebugHelper.log("PluginProxyActivity.initPlugin start loadClass");
}
this.mPluginActivity = (IPluginActivity) this.d.newInstance();
this.mPluginActivity.IInit(this.i, this.g, this, a, packageInfoWithException, this.e, this.f);
Intent intent = new Intent(getIntent());
Bundle bundleExtra = intent.getBundleExtra(INNER_INTENT_EXTRAS);
if (bundleExtra != null) {
intent.putExtras(bundleExtra);
intent.removeExtra(INNER_INTENT_EXTRAS);
}
this.mPluginActivity.ISetIntent(intent);
}

分析com.tencent.mobileqq.pluginsdk.PluginStatic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
public abstract class PluginStatic {
public static final String PARAM_CLASS_STATISTICS_UPLOADER = "clsUploader";
public static final String PARAM_CLEAR_TOP = "cleartop";
public static final String PARAM_EXTRA_INFO = "pluginsdk_extraInfo";
public static final String PARAM_LAUNCH_ACTIVITY = "pluginsdk_launchActivity";
public static final String PARAM_LAUNCH_SERVICE = "pluginsdk_launchService";
public static final String PARAM_PATH = "pluginsdk_pluginpath";
public static final String PARAM_PLUGIN_GESTURELOCK = "param_plugin_gesturelock";
public static final String PARAM_PLUGIN_INTERNAL_ACTIVITIES_ONLY = "PARAM_PLUGIN_INTERNAL_ACTIVITIES_ONLY";
public static final String PARAM_PLUGIN_LOCATION = "pluginsdk_pluginLocation";
public static final String PARAM_PLUGIN_NAME = "pluginsdk_pluginName";
public static final String PARAM_PLUGIN_RECEIVER_CLASS_NAME = "pluginsdk_launchReceiver";
public static final String PARAM_UIN = "pluginsdk_selfuin";
public static final String PARAM_USE_QQ_RESOURCES = "userQqResources";
public static final String PARAM_USE_SKIN_ENGINE = "useSkinEngine";
public static final int USER_QQ_RESOURCES_BOTH = 2;
public static final int USER_QQ_RESOURCES_NO = -1;
public static final int USER_QQ_RESOURCES_YES = 1;
static final String a = "com.tencent.mobileqq";
static final String b = "pluginsdk_IsPluginActivity";
static final ConcurrentHashMap c = new ConcurrentHashMap();
static final ConcurrentHashMap d = new ConcurrentHashMap();
private static final HashMap e = new HashMap();
private static ArrayList f = new ArrayList();

public interface IPluginLife {
void onLoad();

void onUnload();
}
//上下文 读取 插件的id,插件apk的路径
static synchronized ClassLoader a(Context context, String str, String str2) throws Exception {
ClassLoader classLoader;
synchronized (PluginStatic.class) {
classLoader = (DexClassLoader) c.get(str);
if (classLoader == null) {
ClassLoader soDexClassLoader;
QLog.d("plugin_tag", 1, "getOrCreateClassLoader:" + str);
long currentTimeMillis = System.currentTimeMillis();
String canonicalPath = PluginUtils.getOptimizedDexPath(context).getCanonicalPath();
String canonicalPath2 = PluginUtils.getPluginLibPath(context, str).getCanonicalPath();
if (str2.endsWith(".so")) {
soDexClassLoader = new SoDexClassLoader(str2, canonicalPath, canonicalPath2, context.getClassLoader());
} else {
if (str.startsWith("qzone_live_video_plugin")) {
long currentTimeMillis2 = System.currentTimeMillis();
ClassLoader orCreateClassLoader = getOrCreateClassLoader(context, "qzone_plugin.apk");
QLog.d("plugin_tag", 1, "get qzone classloader cost=" + (System.currentTimeMillis() - currentTimeMillis2));
soDexClassLoader = new QZoneLiveClassLoader(str2, canonicalPath, canonicalPath2, orCreateClassLoader);
} else {
String str3;
if (str2 != null) {
if (!new File(str2).exists()) {
QLog.d("plugin_tag", 1, "getOrCreateClassLoader notExist " + str2);
classLoader = new DexClassLoader("", canonicalPath, canonicalPath2, context.getClassLoader());
}
}
if (PluginUtils.isOsNeedReleaseDex() && IPluginAdapterProxy.getProxy().isSupportMultiDex(str)) {
File multiDexSecondDex = PluginUtils.getMultiDexSecondDex(context, str);
if (multiDexSecondDex.exists()) {
str3 = str2 + File.pathSeparator + multiDexSecondDex.getAbsolutePath();
QLog.d("plugin_tag", 1, "multiDex dexsPath" + str3);
QLog.d("plugin_tag", 1, "dexsPath" + str3);
soDexClassLoader = new DexClassLoader(str3, canonicalPath, canonicalPath2, context.getClassLoader());
}
}
str3 = str2;
QLog.d("plugin_tag", 1, "dexsPath" + str3);
soDexClassLoader = new DexClassLoader(str3, canonicalPath, canonicalPath2, context.getClassLoader());
}
}
PackageInfo packageInfo = (PackageInfo) d.get(str2);
if (packageInfo == null) {
try {
packageInfo = ApkFileParser.getPackageInfoWithException(context, str2, 129);
} catch (Throwable th) {
DebugHelper.log("plugin_tag", "PluginStatic.getOrCreateClassLoaderByPath Get Package Info Failed!", th);
}
if (packageInfo == null) {
DebugHelper.log("PluginStatic.getOrCreateClassLoaderByPath Get Package Info Failed! " + new File(str2).exists());
}
d.put(str2, packageInfo);
}
if (packageInfo != null) {
a(packageInfo, str, soDexClassLoader);
}
QLog.w("plugin_tag", 1, "getOrCreateClassLoaderCost:" + str + " c:" + (System.currentTimeMillis() - currentTimeMillis));
c.put(str, soDexClassLoader);
classLoader = soDexClassLoader;
}
}
return classLoader;
}

static List a() {
return f;
}

static void a(PackageInfo packageInfo, String str, ClassLoader classLoader) {
try {
if (((IPluginLife) e.get(str)) == null && packageInfo != null && packageInfo.applicationInfo != null && packageInfo.applicationInfo.metaData != null) {
String string = packageInfo.applicationInfo.metaData.getString("PLUGIN_LIFE_CLASS");
if (string != null) {
IPluginLife iPluginLife = (IPluginLife) classLoader.loadClass(string).newInstance();
e.put(str, iPluginLife);
iPluginLife.onLoad();
}
}
} catch (Throwable th) {
}
}

static void a(IPluginActivity iPluginActivity) {
b();
synchronized (f) {
f.add(new WeakReference(iPluginActivity));
}
}

static boolean a(Bundle bundle) {
if (bundle == null) {
return false;
}
try {
String string = bundle.getString(PARAM_PLUGIN_LOCATION);
if (TextUtils.isEmpty(string) || string.substring(0, string.lastIndexOf(".")).contains(".") || TextUtils.isEmpty(bundle.getString(PARAM_PLUGIN_NAME))) {
return false;
}
string = bundle.getString(PARAM_PATH);
return !TextUtils.isEmpty(string) ? TextUtils.isEmpty(string) ? true : a(string) : false;
} catch (Throwable th) {
return false;
}
}

static boolean a(String str) {
try {
if (str.contains("..")) {
return false;
}
if (!str.endsWith(".so")) {
return str.endsWith(".apk") ? a(str, PluginUtils.getPluginInstallDir(BaseApplication.getContext())) : false;
} else {
String parent = BaseApplication.getContext().getFilesDir().getParent();
File file = new File(parent + SoLoadCore.PATH_TX_LIB);
if (a(str, new File(parent + SoLoadCore.PATH_LIB)) || a(str, file)) {
}
return true;
}
} catch (Exception e) {
return false;
}
}

private static boolean a(String str, File file) throws IOException {
String canonicalPath = file.getCanonicalPath();
String canonicalPath2 = new File(str).getParentFile().getCanonicalPath();
if (QLog.isColorLevel()) {
QLog.d("plugin_tag", 2, "path:" + str + "-> [" + canonicalPath2 + ", " + canonicalPath + "]");
}
return canonicalPath2.equals(canonicalPath);
}

static void b() {
synchronized (f) {
int i = 0;
while (i < f.size()) {
int i2;
if (((WeakReference) f.get(i)).get() == null) {
f.remove(i);
i2 = i - 1;
} else {
i2 = i;
}
i = i2 + 1;
}
}
}

static void b(IPluginActivity iPluginActivity) {
b();
c(iPluginActivity);
}

public static String byteArrayToHex(byte[] byteArray) {
int i = 0;
char[] cArr = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
char[] cArr2 = new char[(byteArray.length * 2)];
int length = byteArray.length;
int i2 = 0;
while (i < length) {
byte b = byteArray[i];
int i3 = i2 + 1;
cArr2[i2] = cArr[(b >>> 4) & 15];
i2 = i3 + 1;
cArr2[i3] = cArr[b & 15];
i++;
}
return new String(cArr2);
}

private static boolean c(IPluginActivity iPluginActivity) {
synchronized (f) {
for (int i = 0; i < f.size(); i++) {
if (((WeakReference) f.get(i)).get() == iPluginActivity) {
f.remove(i);
return true;
}
}
return false;
}
}

public static String encodeFile(String filePath) {
Throwable e;
String str = "";
File file = new File(filePath);
if (file.exists() && file.isFile()) {
FileInputStream fileInputStream;
try {
byte[] bArr;
MessageDigest instance = MessageDigest.getInstance("MD5");
fileInputStream = new FileInputStream(file);
try {
bArr = new byte[16384];
} catch (OutOfMemoryError e2) {
bArr = new byte[4096];
}
while (true) {
try {
int read = fileInputStream.read(bArr);
if (read == -1) {
break;
}
instance.update(bArr, 0, read);
} catch (Exception e3) {
e = e3;
}
}
String byteArrayToHex = byteArrayToHex(instance.digest());
if (fileInputStream == null) {
return byteArrayToHex;
}
try {
fileInputStream.close();
return byteArrayToHex;
} catch (IOException e4) {
return byteArrayToHex;
}
} catch (Exception e5) {
e = e5;
fileInputStream = null;
try {
if (QLog.isColorLevel()) {
QLog.e("plugin_tag", 2, "encode-Exception:" + QLog.getStackTraceString(e));
}
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e6) {
}
}
return str;
} catch (Throwable th) {
e = th;
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e7) {
}
}
throw e;
}
} catch (Throwable th2) {
e = th2;
fileInputStream = null;
if (fileInputStream != null) {
fileInputStream.close();
}
throw e;
}
}
if (QLog.isColorLevel()) {
QLog.e("plugin_tag", 2, "encode-File does not exist or is not file");
}
return str;
}

public static synchronized ClassLoader getClassLoader(String pluginID) {
DexClassLoader dexClassLoader;
synchronized (PluginStatic.class) {
dexClassLoader = (DexClassLoader) c.get(pluginID);
}
return dexClassLoader;
}
//加载红包插件也是这样玩的,实际上就是加载一个apk的dexclassloader,如果已缓存就从内从中取出,否则就加载一个, 传递plugindId,和 安装路径就饿可到了一个新的classloader
public static synchronized ClassLoader getOrCreateClassLoader(Context c, String pluginID) throws Exception {
ClassLoader classLoader;
synchronized (PluginStatic.class) {
classLoader = (ClassLoader) c.get(pluginID);
if (classLoader == null) {
classLoader = a(c, pluginID, PluginUtils.getInstalledPluginPath(c, pluginID).getCanonicalPath());
}
}
return classLoader;
}

public static List isProcessesExist(Context c, List processNames) {
if (processNames == null) {
return null;
}
List arrayList = new ArrayList();
List<RunningAppProcessInfo> runningAppProcesses = ((ActivityManager) c.getSystemService("activity")).getRunningAppProcesses();
if (runningAppProcesses == null) {
for (int i = 0; i < processNames.size(); i++) {
arrayList.add(Boolean.FALSE);
}
return arrayList;
}
for (String str : processNames) {
boolean z;
for (RunningAppProcessInfo runningAppProcessInfo : runningAppProcesses) {
if (str.equalsIgnoreCase(runningAppProcessInfo.processName)) {
z = true;
break;
}
}
z = false;
arrayList.add(Boolean.valueOf(z));
}
return arrayList;
}

public static synchronized ClassLoader removeClassLoader(String pluginId) {
ClassLoader classLoader;
synchronized (PluginStatic.class) {
QLog.d("plugin_tag", 1, "removeClassLoader:" + pluginId);
classLoader = (ClassLoader) c.remove(pluginId);
}
return classLoader;
}
}

分析QQ如何加载插件apk的so文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

public class PluginUtils {
public static final String CONFIG_FILE_EXTEND_NAME = ".cfg";
private static final int a = 8192;
private static Map b = new ConcurrentHashMap();
private static Map c = new ConcurrentHashMap();
private static final String d = ".tmp";

static class a extends Exception {
private static final long a = 1;

public a(String str) {
super(str);
}
}

static class b extends Exception {
private static final long a = 1;

b() {
}
}


public static String getExceptionInfo(Throwable t) {
while (t.getCause() != null) {
t = t.getCause();
}
Writer stringWriter = new StringWriter();
t.printStackTrace(new PrintWriter(stringWriter, true));
return stringWriter.getBuffer().toString();
}
}

从上面的源码可以看出来extractLibs方法做了这个事情,首先把apk文件读入到QZipFile,然后 得到实体遍历文件夹得到真正的apk路径。

上面的代码目前还无法得知干嘛的,暂时放一放谈谈pluginId,qq定义为一个没有路径的apk文件名

1
2
3
public String getPluginID() {
return "qwallet_plugin.apk";
}
1
2

腾讯QQ的插件activity接口

public interface IPluginActivity {
boolean IDispatchTouchEvent(MotionEvent motionEvent);

void IFinish();

View IGetContentView();

Handler IGetInHandler();

Resources IGetResource();

void IInit(String str, String str2, Activity activity, ClassLoader classLoader, PackageInfo packageInfo, boolean z, int i);

boolean IIsWrapContent();

void IOnActivityResult(int i, int i2, Intent intent);

void IOnAttachFragment(Fragment fragment);

boolean IOnBackPressed();

void IOnConfigurationChanged(Configuration configuration);

void IOnCreate(Bundle bundle);

boolean IOnCreateOptionsMenu(Menu menu);

void IOnDestroy();

boolean IOnKeyDown(int i, KeyEvent keyEvent);

boolean IOnKeyMultiple(int i, int i2, KeyEvent keyEvent);

boolean IOnKeyUp(int i, KeyEvent keyEvent);

boolean IOnMenuItemSelected(int i, MenuItem menuItem);

void IOnNewIntent(Intent intent);

boolean IOnOptionsItemSelected(MenuItem menuItem);

void IOnPause();

boolean IOnPrepareOptionsMenu(Menu menu);

void IOnRestart();

void IOnRestoreInstanceState(Bundle bundle);

void IOnResume();

void IOnSaveInstanceState(Bundle bundle);

void IOnSetTheme();

void IOnStart();

void IOnStop();

boolean IOnTouchEvent(MotionEvent motionEvent);

void IOnUserInteraction();

void IOnWindowFocusChanged(boolean z);

void ISetIntent(Intent intent);

void ISetIsTab();

void ISetOutHandler(Handler handler);

void ISetParent(BasePluginActivity basePluginActivity);

ImmersiveConfig IgetImmersiveConfig();

}

1
2
3
4
5
6
7
8
9
10
实现类```BasePluginActivity```

![image.png](https://upload-images.jianshu.io/upload_images/2815884-f6d4592c4b971cca.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
this.f就是插件apk,说明如果是插件模式的话就调用的是this.f ,也就是说这个插件apk是可以单独运行的。

还是不看了,感觉由于不能跟踪代码,看起来头晕
项目庞大,一般的分析工具都卡死了。


再谈谈android 的其他hook 如破解签名hook

interface IPackageManager {
PackageInfo getPackageInfo(String packageName, int flags);
int getPackageUid(String packageName);
int[] getPackageGids(String packageName);

String[] currentToCanonicalPackageNames(in String[] names);
String[] canonicalToCurrentPackageNames(in String[] names);

PermissionInfo getPermissionInfo(String name, int flags);

List<PermissionInfo> queryPermissionsByGroup(String group, int flags);

PermissionGroupInfo getPermissionGroupInfo(String name, int flags);

List<PermissionGroupInfo> getAllPermissionGroups(int flags);

ApplicationInfo getApplicationInfo(String packageName, int flags);

ActivityInfo getActivityInfo(in ComponentName className, int flags);

ActivityInfo getReceiverInfo(in ComponentName className, int flags);

ServiceInfo getServiceInfo(in ComponentName className, int flags);

ProviderInfo getProviderInfo(in ComponentName className, int flags);

int checkPermission(String permName, String pkgName);

int checkUidPermission(String permName, int uid);

boolean addPermission(in PermissionInfo info);

void removePermission(String name);

boolean isProtectedBroadcast(String actionName);

int checkSignatures(String pkg1, String pkg2);

int checkUidSignatures(int uid1, int uid2);

String[] getPackagesForUid(int uid);

String getNameForUid(int uid);

int getUidForSharedUser(String sharedUserName);

ResolveInfo resolveIntent(in Intent intent, String resolvedType, int flags);

List<ResolveInfo> queryIntentActivities(in Intent intent, 
        String resolvedType, int flags);

List<ResolveInfo> queryIntentActivityOptions(
        in ComponentName caller, in Intent[] specifics,
        in String[] specificTypes, in Intent intent,
        String resolvedType, int flags);

List<ResolveInfo> queryIntentReceivers(in Intent intent,
        String resolvedType, int flags);

ResolveInfo resolveService(in Intent intent,
        String resolvedType, int flags);

List<ResolveInfo> queryIntentServices(in Intent intent,
        String resolvedType, int flags);

ParceledListSlice getInstalledPackages(int flags, in String lastRead);


ParceledListSlice getInstalledApplications(int flags, in String lastRead);


List<ApplicationInfo> getPersistentApplications(int flags);

ProviderInfo resolveContentProvider(String name, int flags);


void querySyncProviders(inout List<String> outNames,
        inout List<ProviderInfo> outInfo);

List<ProviderInfo> queryContentProviders(
        String processName, int uid, int flags);

InstrumentationInfo getInstrumentationInfo(
        in ComponentName className, int flags);

List<InstrumentationInfo> queryInstrumentation(
        String targetPackage, int flags);

void installPackage(in Uri packageURI, IPackageInstallObserver observer, int flags,
        in String installerPackageName);

void finishPackageInstall(int token);

n performDexOpt(String packageName);

void updateExternalMediaStatus(boolean mounted, boolean reportStatus);

String nextPackageToClean(String lastPackage);

void movePackage(String packageName, IPackageMoveObserver observer, int flags);

boolean addPermissionAsync(in PermissionInfo info);

boolean setInstallLocation(int loc);
int getInstallLocation();

}

1
2

hook

public class IPackageManagerHook extends ProxyHook {

private static final String TAG = IPackageManagerHook.class.getSimpleName();

public IPackageManagerHook(Context hostContext) {
    super(hostContext);
}

@Override
protected BaseHookHandle createHookHandle() {
    return new IPackageManagerHookHandle(mHostContext);
}

@Override
protected void onInstall(ClassLoader classLoader) throws Throwable {
    Object currentActivityThread = ActivityThreadCompat.currentActivityThread();

    setOldObj(FieldUtils.readField(currentActivityThread, "sPackageManager"));
    Class<?> iPmClass = mOldObj.getClass();
    List<Class<?>> interfaces = Utils.getAllInterfaces(iPmClass);
    Class[] ifs = interfaces != null && interfaces.size() > 0 ? interfaces.toArray(new Class[interfaces.size()]) : new Class[0];
    Object newPm = MyProxy.newProxyInstance(iPmClass.getClassLoader(), ifs, this);
    FieldUtils.writeField(currentActivityThread, "sPackageManager", newPm);
    PackageManager pm = mHostContext.getPackageManager();
    Object mPM = FieldUtils.readField(pm, "mPM");
    if (mPM != newPm) {
        FieldUtils.writeField(pm, "mPM", newPm);
    }
}


public static void fixContextPackageManager(Context context) {
    try {
        Object currentActivityThread = ActivityThreadCompat.currentActivityThread();
        Object newPm = FieldUtils.readField(currentActivityThread, "sPackageManager");
        PackageManager pm = context.getPackageManager();
        Object mPM = FieldUtils.readField(pm, "mPM");
        if (mPM != newPm) {
            FieldUtils.writeField(pm, "mPM", newPm);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

}

1
hook启动的另外一一种方式

public class InstrumentationHook extends Hook {

private static final String TAG = InstrumentationHook.class.getSimpleName();
private List<PluginInstrumentation> mPluginInstrumentations = new ArrayList<PluginInstrumentation>();

public InstrumentationHook(Context hostContext) {
    super(hostContext);
}

@Override
protected BaseHookHandle createHookHandle() {
    return null;
}

@Override
public void setEnable(boolean enable, boolean reinstallHook) {
    if (reinstallHook) {
        try {
            onInstall(null);
        } catch (Throwable throwable) {
            Log.i(TAG, "setEnable onInstall fail", throwable);
        }
    }

    for (PluginInstrumentation pit : mPluginInstrumentations) {
        pit.setEnable(enable);
    }

    super.setEnable(enable,reinstallHook);
}

@Override
protected void onInstall(ClassLoader classLoader) throws Throwable {

    Object target = ActivityThreadCompat.currentActivityThread();
    Class ActivityThreadClass = ActivityThreadCompat.activityThreadClass();

     /*替换ActivityThread.mInstrumentation,拦截组件调度消息*/
    Field mInstrumentationField = FieldUtils.getField(ActivityThreadClass, "mInstrumentation");
    Instrumentation mInstrumentation = (Instrumentation) FieldUtils.readField(mInstrumentationField, target);
    if (!PluginInstrumentation.class.isInstance(mInstrumentation)) {
        PluginInstrumentation pit = new PluginInstrumentation(mHostContext, mInstrumentation);
        pit.setEnable(isEnable());
        mPluginInstrumentations.add(pit);
        FieldUtils.writeField(mInstrumentationField, target, pit);
        Log.i(TAG, "Install Instrumentation Hook old=%s,new=%s", mInstrumentationField, pit);
    } else {
        Log.i(TAG, "Instrumentation has installed,skip");
    }
}

}