/*
* Copyright (C) 2015 HouKx <hkx.aidream@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.pluginmgr;
import static java.lang.reflect.Modifier.FINAL;
import static java.lang.reflect.Modifier.PRIVATE;
import static java.lang.reflect.Modifier.PROTECTED;
import static java.lang.reflect.Modifier.PUBLIC;
import static java.lang.reflect.Modifier.STATIC;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Bundle;
import android.util.DisplayMetrics;
import com.google.dexmaker.Code;
import com.google.dexmaker.Comparison;
import com.google.dexmaker.DexMaker;
import com.google.dexmaker.FieldId;
import com.google.dexmaker.Label;
import com.google.dexmaker.Local;
import com.google.dexmaker.MethodId;
import com.google.dexmaker.TypeId;
import com.google.dexmaker.dx.dex.DexFormat;
/**
* 动态生成 插件Activity子类的工具类
*
* @author HouKangxi
*
*/
class ActivityClassGenerator {
private static final String FIELD_ASSERTMANAGER = "mAssertManager";
private static final String FIELD_RESOURCES = "mResources";
public static void createActivityDex(String superClassName,
String targetClassName, File saveTo, String pluginId, String pkgName)
throws IOException {
byte[] dex = createActivityDex(superClassName, targetClassName,
pluginId, pkgName);
if (saveTo.getName().endsWith(".dex")) {
FileUtil.writeToFile(dex, saveTo);
} else {
JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(
saveTo));
jarOut.putNextEntry(new JarEntry(DexFormat.DEX_IN_JAR_NAME));
jarOut.write(dex);
jarOut.closeEntry();
jarOut.close();
}
}
/**
*
* @param superClassName
* @param targetClassName
* @param pluginId
* @param pkgName
* @return
*/
public static <S, D extends S> byte[] createActivityDex(
final String superClassName, final String targetClassName,
final String pluginId, String pkgName) {
DexMaker dexMaker = new DexMaker();
TypeId<D> generatedType = TypeId.get('L' + targetClassName.replace('.',
'/') + ';');
TypeId<S> superType = TypeId
.get('L' + superClassName.replace('.', '/') + ';');
// 声明类
dexMaker.declare(generatedType, "", PUBLIC | FINAL, superType);
// 定义字段
// private static final String _pluginId = @param{pluginId};
// private AssetManager asm;
// private Resources res;
declareFields(dexMaker, generatedType, superType, pluginId);
// 声明 默认构造方法
declare_constructor(dexMaker, generatedType, superType);
// 声明 方法:onCreate
declareMethod_onCreate(dexMaker, generatedType, superType);
// 声明 方法:public AssetManager getAssets()
declareMethod_getAssets(dexMaker, generatedType, superType);
// 声明 方法:public Resources getResources()
declareMethod_getResources(dexMaker, generatedType, superType);
/*
* 声明 方法:startActivityForResult(Intent intent, int requestCode, Bundle
* options)
*/
declareMethod_startActivityForResult(dexMaker, generatedType, superType);
// 声明 方法:public void onBackPressed()
declareMethod_onBackPressed(dexMaker, generatedType, superType);
declareMethod_startService(dexMaker, generatedType, superType);
declareMethod_bindService(dexMaker, generatedType, superType);
declareMethod_unbindService(dexMaker, generatedType, superType);
declareMethod_stopService(dexMaker, generatedType, superType);
// declareMethod_getPackageName(dexMaker, generatedType, superType,
// pkgName);
// Create the dex Content
declareLifeCyleMethod(dexMaker, generatedType, superType, "onResume");
declareLifeCyleMethod(dexMaker, generatedType, superType, "onStart");
declareLifeCyleMethod(dexMaker, generatedType, superType, "onRestart");
declareLifeCyleMethod(dexMaker, generatedType, superType, "onPause");
declareLifeCyleMethod(dexMaker, generatedType, superType, "onStop");
declareLifeCyleMethod(dexMaker, generatedType, superType, "onDestroy");
byte[] dex = dexMaker.generate();
return dex;
}
private static <S, D extends S> void declareFields(DexMaker dexMaker,
TypeId<D> generatedType, TypeId<S> superType, String pluginId) {
FieldId<D, String> _pluginId = generatedType.getField(TypeId.STRING,
"_pluginId");
dexMaker.declare(_pluginId, PRIVATE | STATIC | FINAL, pluginId);
TypeId<AssetManager> AssetManager = TypeId.get(AssetManager.class);
TypeId<Resources> Resources = TypeId.get(Resources.class);
FieldId<D, AssetManager> asm = generatedType.getField(AssetManager,
FIELD_ASSERTMANAGER);
dexMaker.declare(asm, PRIVATE, null);
FieldId<D, Resources> res = generatedType.getField(Resources,
FIELD_RESOURCES);
dexMaker.declare(res, PRIVATE, null);
}
// Note: 必须是最后一个Local变量处调用
private static <D> Local<String> get_pluginId(TypeId<D> generatedType,
Code methodCode) {
Local<String> pluginId = methodCode.newLocal(TypeId.STRING);
FieldId<D, String> fieldId = generatedType.getField(TypeId.STRING,
"_pluginId");
methodCode.sget(fieldId, pluginId);
return pluginId;
}
// private static <S,D extends S> void declareMethod_getPackageName(
// DexMaker dexMaker, TypeId<D> generatedType, TypeId<S> superType,
// String pkgName) {
// MethodId<D, String> methodOveride = generatedType.getMethod(
// TypeId.STRING, "getPackageName");
// Code methodCode = dexMaker.declare(methodOveride, PUBLIC);
// Local<String> local = methodCode.newLocal(TypeId.STRING);
// methodCode.loadConstant(local, pkgName);
// methodCode.returnValue(local);
// }
@SuppressWarnings({ "unchecked", "rawtypes" })
private static <S, D extends S> void declareMethod_onCreate(
DexMaker dexMaker, TypeId<D> generatedType, TypeId<S> superType) {
TypeId<AssetManager> AssetManager = TypeId.get(AssetManager.class);
TypeId<Resources> Resources = TypeId.get(Resources.class);
// TypeId<Theme> Theme = TypeId.get(Theme.class);
FieldId<D, AssetManager> assertManager = generatedType.getField(
AssetManager, FIELD_ASSERTMANAGER);
FieldId<D, Resources> resources = generatedType.getField(Resources,
FIELD_RESOURCES);
//
// 声明 方法:onCreate
TypeId<Bundle> Bundle = TypeId.get(Bundle.class);
TypeId<ActivityOverider> ActivityOverider = TypeId
.get(ActivityOverider.class);
TypeId<DisplayMetrics> DisplayMetrics = TypeId
.get(DisplayMetrics.class);
TypeId<Configuration> Configuration = TypeId.get(Configuration.class);
MethodId<D, Void> method = generatedType.getMethod(TypeId.VOID,
"onCreate", Bundle);
Code methodCode = dexMaker.declare(method, PROTECTED);
// locals -- 一个方法内的本地变量必须提前声明在所有操作之前
Local<D> localThis = methodCode.getThis(generatedType);
Local<Bundle> lcoalBundle = methodCode.getParameter(0, Bundle);
Local<AssetManager> localAsm = methodCode.newLocal(AssetManager);
Local<Resources> superRes = methodCode.newLocal(Resources);
Local<DisplayMetrics> mtrc = methodCode.newLocal(DisplayMetrics);
Local<Configuration> cfg = methodCode.newLocal(Configuration);
Local<Resources> resLocal = methodCode.newLocal(Resources);
// Local<Theme> localTheme = methodCode.newLocal(Theme);
// Local<Theme> superTheme = methodCode.newLocal(Theme);
Local<String> pluginId = get_pluginId(generatedType, methodCode);
// ActivityOverider:
// public static AssetManager getAssetManager(String,Activity)
MethodId<ActivityOverider, AssetManager> methodOveride = ActivityOverider
.getMethod(AssetManager, "getAssetManager", TypeId.STRING,
TypeId.get(Activity.class));
//
methodCode.invokeStatic(methodOveride, localAsm, pluginId, localThis);
methodCode.iput(assertManager, localThis, localAsm);
MethodId methodGetResources = superType.getMethod(Resources,
"getResources");
methodCode.invokeSuper(methodGetResources, superRes, localThis);
//
// superRes.getDisplayMetrics()
MethodId<Resources, DisplayMetrics> getDisplayMetrics = Resources
.getMethod(DisplayMetrics, "getDisplayMetrics");
methodCode.invokeVirtual(getDisplayMetrics, mtrc, superRes);
//
// superRes.getConfiguration()
MethodId<Resources, Configuration> getConfiguration = Resources
.getMethod(Configuration, "getConfiguration");
methodCode.invokeVirtual(getConfiguration, cfg, superRes);
//
// res = new Resources(asm, superRes.getDisplayMetrics(),
// superRes.getConfiguration());
MethodId<Resources, Void> res_constructor = Resources.getConstructor(
AssetManager, DisplayMetrics, Configuration);
methodCode.newInstance(resLocal, res_constructor, localAsm, mtrc, cfg);
methodCode.iput(resources, localThis, resLocal);
MethodId<ActivityOverider, Void> method_call_onCreate = ActivityOverider
.getMethod(TypeId.VOID, "callback_onCreate", TypeId.STRING,
TypeId.get(Activity.class));
methodCode
.invokeStatic(method_call_onCreate, null, pluginId, localThis);
// super.onCreate()
MethodId superMethod = superType.getMethod(TypeId.VOID, "onCreate",
Bundle);
methodCode.invokeSuper(superMethod, null, localThis, lcoalBundle);
methodCode.returnVoid();
}
private static <S, D extends S> void declareMethod_getResources(
DexMaker dexMaker, TypeId<D> generatedType, TypeId<S> superType) {
TypeId<Resources> Resources = TypeId.get(Resources.class);
MethodId<D, Resources> getResources = generatedType.getMethod(
Resources, "getResources");
Code code = dexMaker.declare(getResources, PUBLIC);
Local<D> localThis = code.getThis(generatedType);
Local<Resources> localRes = code.newLocal(Resources);
Local<Resources> nullV = code.newLocal(Resources);
code.loadConstant(nullV, null);
FieldId<D, Resources> res = generatedType.getField(Resources,
FIELD_RESOURCES);
code.iget(res, localRes, localThis);
//
Label localResIsNull = new Label();
code.compare(Comparison.NE, localResIsNull, localRes, nullV);
MethodId<S, Resources> superGetResources = superType.getMethod(
Resources, "getResources");
code.invokeSuper(superGetResources, localRes, localThis);
code.mark(localResIsNull);
//
code.returnValue(localRes);
}
private static <S, D extends S> void declareMethod_getAssets(
DexMaker dexMaker, TypeId<D> generatedType, TypeId<S> superType) {
TypeId<AssetManager> AssetManager = TypeId.get(AssetManager.class);
MethodId<D, AssetManager> getAssets = generatedType.getMethod(
AssetManager, "getAssets");
Code code = dexMaker.declare(getAssets, PUBLIC);
Local<D> localThis = code.getThis(generatedType);
Local<AssetManager> localAsm = code.newLocal(AssetManager);
Local<AssetManager> nullV = code.newLocal(AssetManager);
code.loadConstant(nullV, null);
FieldId<D, AssetManager> res = generatedType.getField(AssetManager,
FIELD_ASSERTMANAGER);
code.iget(res, localAsm, localThis);
Label localAsmIsNull = new Label();
code.compare(Comparison.NE, localAsmIsNull, localAsm, nullV);
MethodId<S, AssetManager> superGetAssetManager = superType.getMethod(
AssetManager, "getAssets");
code.invokeSuper(superGetAssetManager, localAsm, localThis);
code.mark(localAsmIsNull);
code.returnValue(localAsm);
}
private static <S, D extends S> void declare_constructor(DexMaker dexMaker,
TypeId<D> generatedType, TypeId<S> superType) {
MethodId<D, Void> method = generatedType.getConstructor();
Code constructorCode = dexMaker.declare(method, PUBLIC);
Local<D> localThis = constructorCode.getThis(generatedType);
MethodId<S, Void> superConstructor = superType.getConstructor();
constructorCode.invokeDirect(superConstructor, null, localThis);
constructorCode.returnVoid();// void 方法也必须显式的声明返回void
}
private static <S, D extends S> void declareMethod_startActivityForResult(
DexMaker dexMaker, TypeId<D> generatedType, TypeId<S> superType) {
TypeId<Intent> intent = TypeId.get(Intent.class);
TypeId<Integer> requestCode = TypeId.INT;
TypeId<Bundle> bundle = TypeId.get(Bundle.class);
TypeId<?>[] params;
String methodName = "startActivityForResult";
final boolean isNewSdk = android.os.Build.VERSION.SDK_INT > 10;
if (isNewSdk) {
params = new TypeId[] { intent, requestCode, bundle };
} else {
params = new TypeId[] { intent, requestCode };
}
MethodId<D, Void> method = generatedType.getMethod(TypeId.VOID,
methodName, params);
MethodId<S, Void> superMethod = superType.getMethod(TypeId.VOID,
methodName, params);
Code methodCode = dexMaker.declare(method, PUBLIC);
TypeId<ActivityOverider> ActivityOverider = TypeId
.get(ActivityOverider.class);
MethodId<ActivityOverider, Intent> methodOveride = ActivityOverider
.getMethod(intent, "overrideStartActivityForResult",
TypeId.get(Activity.class), TypeId.STRING, intent,
requestCode, bundle);
// locals
Local<D> localThis = methodCode.getThis(generatedType);
Local<Intent> newIntent = methodCode.newLocal(intent);
Local<Bundle> nullParamBundle = methodCode.newLocal(bundle);
Local<String> pluginId = get_pluginId(generatedType, methodCode);
methodCode.loadConstant(nullParamBundle, null);
Local<?> args[];
if (isNewSdk) {
args = new Local[] { localThis, pluginId,
methodCode.getParameter(0, intent)//
, methodCode.getParameter(1, requestCode)//
, methodCode.getParameter(2, bundle) //
};
methodCode.invokeStatic(methodOveride, newIntent, args);
// super.startActivityForResult(...)
methodCode.invokeSuper(superMethod, null, localThis//
, newIntent//
, methodCode.getParameter(1, requestCode)//
, methodCode.getParameter(2, bundle) //
);
} else {
args = new Local[] { localThis, pluginId,
methodCode.getParameter(0, intent)//
, methodCode.getParameter(1, requestCode)//
, nullParamBundle };
methodCode.invokeStatic(methodOveride, newIntent, args);
methodCode.invokeSuper(superMethod, null, localThis//
, newIntent//
, methodCode.getParameter(1, requestCode)//
);
}
methodCode.returnVoid();
}
/**
* 生成以下代码: <br/>
*
* <pre>
* public void onBackPressed() {
* if (ActivityOverider.overrideOnbackPressed(this, pluginId)) {
* super.onBackPressed();
* }
* }
* </pre>
*
* @param dexMaker
* @param generatedType
* @param superType
*/
private static <S, D extends S> void declareMethod_onBackPressed(
DexMaker dexMaker, TypeId<D> generatedType, TypeId<S> superType) {
TypeId<ActivityOverider> ActivityOverider = TypeId
.get(ActivityOverider.class);
MethodId<D, Void> method = generatedType.getMethod(TypeId.VOID,
"onBackPressed");
Code methodCode = dexMaker.declare(method, PUBLIC);
// locals -- 一个方法内的本地变量必须提前声明在所有操作之前
Local<D> localThis = methodCode.getThis(generatedType);
Local<Boolean> localBool = methodCode.newLocal(TypeId.BOOLEAN);
Local<Boolean> localFalse = methodCode.newLocal(TypeId.BOOLEAN);
Local<String> pluginId = get_pluginId(generatedType, methodCode);
methodCode.loadConstant(localFalse, false);
MethodId<ActivityOverider, Boolean> methodOveride = ActivityOverider
.getMethod(TypeId.BOOLEAN, "overrideOnbackPressed",
TypeId.get(Activity.class), TypeId.STRING);
methodCode.invokeStatic(methodOveride, localBool, localThis, pluginId);
// codeBlock: if start
Label localBool_isInvokeSuper = new Label();
methodCode.compare(Comparison.EQ, localBool_isInvokeSuper, localBool,
localFalse);
MethodId<S, Void> superMethod = superType.getMethod(TypeId.VOID,
"onBackPressed");
methodCode.invokeSuper(superMethod, null, localThis);
methodCode.mark(localBool_isInvokeSuper);
// codeBlock: if end
methodCode.returnVoid();
}
private static <S, D extends S> void declareMethod_startService(
DexMaker dexMaker, TypeId<D> generatedType, TypeId<S> superType) {
TypeId<ActivityOverider> ActivityOverider = TypeId
.get(ActivityOverider.class);
TypeId<ComponentName> returnType = TypeId.get(ComponentName.class);
TypeId<Intent> Intent = TypeId.get(Intent.class);
MethodId<D, ComponentName> method = generatedType.getMethod(returnType,
"startService", Intent);
MethodId<ActivityOverider, ComponentName> methodOveride = ActivityOverider
.getMethod(returnType, "overrideStartService",
TypeId.get(Activity.class), TypeId.STRING, Intent);
Code methodCode = dexMaker.declare(method, PUBLIC);
// locals
Local<D> localThis = methodCode.getThis(generatedType);
Local<ComponentName> localComponentName = methodCode
.newLocal(returnType);
Local<String> pluginId = get_pluginId(generatedType, methodCode);
methodCode.invokeStatic(methodOveride, localComponentName//
, localThis, pluginId, methodCode.getParameter(0, Intent));
methodCode.returnValue(localComponentName);
}
private static <S, D extends S> void declareMethod_bindService(
DexMaker dexMaker, TypeId<D> generatedType, TypeId<S> superType) {
// boolean bindService(intent, conn, flags);
TypeId<ActivityOverider> ActivityOverider = TypeId
.get(ActivityOverider.class);
TypeId<Boolean> returnType = TypeId.BOOLEAN;
TypeId<Intent> Intent = TypeId.get(Intent.class);
TypeId<ServiceConnection> Conn = TypeId.get(ServiceConnection.class);
MethodId<D, Boolean> method = generatedType.getMethod(returnType,
"bindService", Intent, Conn, TypeId.INT);
MethodId<ActivityOverider, Boolean> methodOveride = ActivityOverider
.getMethod(returnType, "overrideBindService",
TypeId.get(Activity.class), TypeId.STRING, Intent,
Conn, TypeId.INT);
Code methodCode = dexMaker.declare(method, PUBLIC);
// locals
Local<D> localThis = methodCode.getThis(generatedType);
Local<Boolean> localBool = methodCode.newLocal(returnType);
Local<String> pluginId = get_pluginId(generatedType, methodCode);
methodCode.invokeStatic(
methodOveride,
localBool//
, localThis, pluginId, methodCode.getParameter(0, Intent),
methodCode.getParameter(1, Conn),
methodCode.getParameter(2, TypeId.INT));
methodCode.returnValue(localBool);
}
private static <S, D extends S> void declareMethod_unbindService(
DexMaker dexMaker, TypeId<D> generatedType, TypeId<S> superType) {
// void unbindService( conn);
TypeId<ActivityOverider> ActivityOverider = TypeId
.get(ActivityOverider.class);
TypeId<ServiceConnection> Conn = TypeId.get(ServiceConnection.class);
MethodId<D, Void> method = generatedType.getMethod(TypeId.VOID,
"unbindService", Conn);
MethodId<ActivityOverider, Void> methodOveride = ActivityOverider
.getMethod(TypeId.VOID, "overrideUnbindService",
TypeId.get(Activity.class), TypeId.STRING, Conn);
Code methodCode = dexMaker.declare(method, PUBLIC);
// locals
Local<D> localThis = methodCode.getThis(generatedType);
Local<String> pluginId = get_pluginId(generatedType, methodCode);
methodCode.invokeStatic(methodOveride, null//
, localThis, pluginId, methodCode.getParameter(0, Conn));
methodCode.returnVoid();
}
private static <S, D extends S> void declareMethod_stopService(
DexMaker dexMaker, TypeId<D> generatedType, TypeId<S> superType) {
// boolean stopService(intent);
TypeId<ActivityOverider> ActivityOverider = TypeId
.get(ActivityOverider.class);
TypeId<Boolean> returnType = TypeId.BOOLEAN;
TypeId<Intent> Intent = TypeId.get(Intent.class);
//
MethodId<D, Boolean> method = generatedType.getMethod(returnType,
"stopService", Intent);
MethodId<ActivityOverider, Boolean> methodOveride = ActivityOverider
.getMethod(returnType, "overrideStopService",
TypeId.get(Activity.class), TypeId.STRING, Intent);
Code methodCode = dexMaker.declare(method, PUBLIC);
// locals
Local<D> localThis = methodCode.getThis(generatedType);
Local<Boolean> localBool = methodCode.newLocal(returnType);
Local<String> pluginId = get_pluginId(generatedType, methodCode);
methodCode.invokeStatic(methodOveride, localBool//
, localThis, pluginId, methodCode.getParameter(0, Intent));
methodCode.returnValue(localBool);
}
private static <S, D extends S> void declareLifeCyleMethod(
DexMaker dexMaker, TypeId<D> generatedType, TypeId<S> superType,
String methodName) {
TypeId<ActivityOverider> ActivityOverider = TypeId
.get(ActivityOverider.class);
MethodId<D, Void> method = generatedType.getMethod(TypeId.VOID,
methodName);
Code methodCode = dexMaker.declare(method, PROTECTED);
// locals
Local<D> localThis = methodCode.getThis(generatedType);
Local<String> pluginId = get_pluginId(generatedType, methodCode);
MethodId<S, Void> superMethod = superType.getMethod(TypeId.VOID,
methodName);
methodCode.invokeSuper(superMethod, null, localThis);
MethodId<ActivityOverider, Void> methodOveride = ActivityOverider
.getMethod(TypeId.VOID, "callback_" + methodName,
TypeId.STRING, TypeId.get(Activity.class));
methodCode.invokeStatic(methodOveride, null, pluginId, localThis);
methodCode.returnVoid();
}
}