android外掛化在9。0上外掛activity的theme失效問題(VirtualApk)
在使用VirtualApk的時候,發現在android 9。0上,外掛中的Activity配置的theme失效
這個問題和Android系統程式碼修改有關,我們看下9。0前後設定theme的變化在哪裡。
看到ActivityThread中
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java。lang。ClassLoader cl = appContext。getClassLoader();
activity = mInstrumentation。newActivity(
cl, component。getClassName(), r。intent);//。。。省略程式碼
try {
Application app = r。packageInfo。makeApplication(false, mInstrumentation);//。。。省略程式碼
activity。attach(appContext, this, getInstrumentation(), r。token,
r。ident, app, r。intent, r。activityInfo, title, r。parent,
r。embeddedID, r。lastNonConfigurationInstances, config,
r。referrer, r。voiceInteractor, window, r。configCallback);//。。。省略程式碼
int theme = r。activityInfo。getThemeResource();
if (theme != 0) {
activity。setTheme(theme);
}}
在performLaunchActivity中進行了Activity的建立和theme的設定。而這個theme是從引數ActivityClientRecord r獲取,看到performLaunchActivity被呼叫的地方
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {//。。。省略程式碼
Activity a = performLaunchActivity(r, customIntent);}
繼續找到
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog。v(TAG, “>>> handling: ” + codeToString(msg。what));
switch (msg。what) {
case LAUNCH_ACTIVITY: {
Trace。traceBegin(Trace。TRACE_TAG_ACTIVITY_MANAGER, “activityStart”);
final ActivityClientRecord r = (ActivityClientRecord) msg。obj;
r。packageInfo = getPackageInfoNoCheck(
r。activityInfo。applicationInfo, r。compatInfo);
handleLaunchActivity(r, null, “LAUNCH_ACTIVITY”);
Trace。traceEnd(Trace。TRACE_TAG_ACTIVITY_MANAGER);
} break;
在ActivityThread的Handler中呼叫的handleLaunchActivity。正是如此,給了我們hook修改的機會,看到VirtualApk的處理VAInstrumentation中
@Overridepublic boolean handleMessage(Message msg) {
if (msg。what == LAUNCH_ACTIVITY) {
// ActivityClientRecord r
Object r = msg。obj;
try {
Reflector reflector = Reflector。with(r);
Intent intent = reflector。field(“intent”)。get();
intent。setExtrasClassLoader(mPluginManager。getHostContext()。getClassLoader());
ActivityInfo activityInfo = reflector。field(“activityInfo”)。get();
if (PluginUtil。isIntentFromPlugin(intent)) {
int theme = PluginUtil。getTheme(mPluginManager。getHostContext(), intent);
if (theme != 0) {
Log。i(TAG, “resolve theme, current theme:” + activityInfo。theme + “ after :0x” + Integer。toHexString(theme));
activityInfo。theme = theme;
}
}
} catch (Exception e) {
Log。w(TAG, e);
}
Virtual 給ActivityThread中的Handler增加了自己的callback,也就是在系統處理LAUNCH_ACTIVITY訊息時,virtualApk會先處理,獲取到對應的ActivityClientRecord,然後修改activityInfo中的theme為外掛的theme。
那為什麼在9。0後就不行了呢,我們看下9。0這部分的原始碼
呵,好傢伙,根本就沒有LAUNCH_ACTIVITY這個定義了,所以hook失效,根本就沒有設定外掛的theme
那系統是怎麼呼叫的handleLaunchActivity呢?
我們找到LaunchActivityItem
public class LaunchActivityItem extends ClientTransactionItem {
private Intent mIntent;
private int mIdent;
private ActivityInfo mInfo;
private Configuration mCurConfig;
private Configuration mOverrideConfig;
private CompatibilityInfo mCompatInfo;
private String mReferrer;
private IVoiceInteractor mVoiceInteractor;
private int mProcState;
private Bundle mState;
private PersistableBundle mPersistentState;
private List mPendingResults;
private List mPendingNewIntents;
private boolean mIsForward;
private ProfilerInfo mProfilerInfo;
@Override
public void preExecute(ClientTransactionHandler client, IBinder token) {
client。updateProcessState(mProcState, false);
client。updatePendingConfiguration(mCurConfig);
}
@Override
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
Trace。traceBegin(TRACE_TAG_ACTIVITY_MANAGER, “activityStart”);
ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
mPendingResults, mPendingNewIntents, mIsForward,
mProfilerInfo, client);
client。handleLaunchActivity(r, pendingActions, null /* customIntent */);
Trace。traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
這個execute又是在哪執行的呢?找到ActivityStackSupervisor
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
boolean andResume, boolean checkConfig) throws RemoteException {
clientTransaction。addCallback(LaunchActivityItem。obtain(new Intent(r。intent),
System。identityHashCode(r), r。info,
// TODO: Have this take the merged configuration instead of separate global
// and override configs。
mergedConfiguration。getGlobalConfiguration(),
mergedConfiguration。getOverrideConfiguration(), r。compat,
r。launchedFromPackage, task。voiceInteractor, app。repProcState, r。icicle,
r。persistentState, results, newIntents, mService。isNextTransitionForward(),
profilerInfo));
// Set desired final state。
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
lifecycleItem = ResumeActivityItem。obtain(mService。isNextTransitionForward());
} else {
lifecycleItem = PauseActivityItem。obtain();
}
clientTransaction。setLifecycleStateRequest(lifecycleItem);
// Schedule transaction。
mService。getLifecycleManager()。scheduleTransaction(clientTransaction);
我們找到LaunchActivityItem建立的地方,而mService。getLifecycleManager()。scheduleTransaction(clientTransaction);是如何執行的呢
void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
final IApplicationThread client = transaction。getClient();
transaction。schedule();
if (!(client instanceof Binder)) {
// If client is not an instance of Binder - it‘s a remote call and at this point it is
// safe to recycle the object。 All objects used for local calls will be recycled after
// the transaction is executed on client in ActivityThread。
transaction。recycle();
}
}
其實就是執行了 transaction的client的schedule()方法。繼續看到
public void schedule() throws RemoteException {
mClient。scheduleTransaction(this);
}
這裡個client是什麼,會到建立的地方看到
final ClientTransaction clientTransaction = ClientTransaction。obtain(app。thread,
r。appToken);
咱們的IApplicationThread嘛。看到他的scheduleTransaction
@Override
public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
ActivityThread。this。scheduleTransaction(transaction);
}
繼續看到 ActivityThread
void scheduleTransaction(ClientTransaction transaction) {
transaction。preExecute(this);
sendMessage(ActivityThread。H。EXECUTE_TRANSACTION, transaction);
}
這下是不是很清晰了,就是把 ClientTransaction 透過Handler傳送給ActivityThread來處理
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg。obj;
mTransactionExecutor。execute(transaction);
if (isSystem()) {
// Client transactions inside system process are recycled on the client side
// instead of ClientLifecycleManager to avoid being cleared before this
// message is handled。
transaction。recycle();
}
// TODO(lifecycler): Recycle locally scheduled transactions。
break;
看到如何execute
public void execute(ClientTransaction transaction) {
final IBinder token = transaction。getActivityToken();
log(“Start resolving transaction for client: ” + mTransactionHandler + “, token: ” + token);
executeCallbacks(transaction);
executeLifecycleState(transaction);
mPendingActions。clear();
log(“End resolving transaction”);
}
看到executeCallbacks
@VisibleForTesting
public void executeCallbacks(ClientTransaction transaction) {
//。。。省略程式碼
final int size = callbacks。size();
for (int i = 0; i < size; ++i) {
item。execute(mTransactionHandler, token, mPendingActions);
//。。。省略程式碼
}
}
這下清楚了把,把新增的callbacks execute下。我們在回顧下設定的地方
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
boolean andResume, boolean checkConfig) throws RemoteException {
clientTransaction。addCallback(LaunchActivityItem。obtain(new Intent(r。intent),
System。identityHashCode(r), r。info,
// TODO: Have this take the merged configuration instead of separate global
// and override configs。
mergedConfiguration。getGlobalConfiguration(),
mergedConfiguration。getOverrideConfiguration(), r。compat,
r。launchedFromPackage, task。voiceInteractor, app。repProcState, r。icicle,
r。persistentState, results, newIntents, mService。isNextTransitionForward(),
profilerInfo));
// Set desired final state。
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
lifecycleItem = ResumeActivityItem。obtain(mService。isNextTransitionForward());
} else {
lifecycleItem = PauseActivityItem。obtain();
}
clientTransaction。setLifecycleStateRequest(lifecycleItem);
// Schedule transaction。
mService。getLifecycleManager()。scheduleTransaction(clientTransaction);
設定了LaunchActivityItem。而execute執行的就是handleLaunchActivity
@Override
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
Trace。traceBegin(TRACE_TAG_ACTIVITY_MANAGER, “activityStart”);
ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
mPendingResults, mPendingNewIntents, mIsForward,
mProfilerInfo, client);
client。handleLaunchActivity(r, pendingActions, null /* customIntent */);
Trace。traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
到這裡整個流程就穿起來了。 所以知道為什麼VirtualApk在9。0上為什麼設定的theme沒有效果,因為系統啟動的呼叫方式已經發生了改變。
那現在我們如何去修改呢。同理看到Handler處理的地方
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg。obj;
mTransactionExecutor。execute(transaction);
if (isSystem()) {
// Client transactions inside system process are recycled on the client side
// instead of ClientLifecycleManager to avoid being cleared before this
// message is handled。
transaction。recycle();
}
// TODO(lifecycler): Recycle locally scheduled transactions。
break;
我們可以獲取到 ClientTransaction。 然後再反射獲取到 mActivityCallbacks。判斷如果是LaunchActivityItem。 繼續反射獲取到ActivityInfo。這裡附上完整的修改程式碼
@Override
public boolean handleMessage(Message msg) {
if (msg。what == LAUNCH_ACTIVITY) {
// ActivityClientRecord r
Object r = msg。obj;
try {
Reflector reflector = Reflector。with(r);
Intent intent = reflector。field(“intent”)。get();
intent。setExtrasClassLoader(mPluginManager。getHostContext()。getClassLoader());
ActivityInfo activityInfo = reflector。field(“activityInfo”)。get();
if (PluginUtil。isIntentFromPlugin(intent)) {
int theme = PluginUtil。getTheme(mPluginManager。getHostContext(), intent);
if (theme != 0) {
Log。i(TAG, “resolve theme, current theme:” + activityInfo。theme + “ after :0x” + Integer。toHexString(theme));
activityInfo。theme = theme;
}
}
} catch (Exception e) {
Log。w(TAG, e);
}
} else if (msg。what == 159) {
//r實際為clienttransaction
Object r = msg。obj;
try {
Class clientClazz = r。getClass();
Field fCallbacks = clientClazz。getDeclaredField(“mActivityCallbacks”);
fCallbacks。setAccessible(true);
//得到transactionz中的callbacks,為一個list,獲取其中元素為LaunchActivityItem
List lists = (List) fCallbacks。get(r);
for (int i = 0; i < lists。size(); i++) {
Object item = lists。get(i);
Class itemClazz = item。getClass();
Log。w(TAG, “class——->” + itemClazz。getName());
if (!(itemClazz。getSimpleName()。equals(“LaunchActivityItem”))) {
return false;
}
//獲取成員 mIntent
Log。w(TAG, “=======get ” + itemClazz。getName());
Field mIntent = itemClazz。getDeclaredField(“mIntent”);
mIntent。setAccessible(true);
Intent intent = (Intent) mIntent。get(item);
Log。w(TAG, “=======get intent ” + intent);
//ActivityInfo mInfo
Field activityInfoField = itemClazz。getDeclaredField(“mInfo”);
activityInfoField。setAccessible(true);
ActivityInfo activityInfo = (ActivityInfo) activityInfoField。get(item);
Log。w(TAG, “=======get ActivityInfo ” + activityInfoField);
intent。setExtrasClassLoader(mPluginManager。getHostContext()。getClassLoader());
if (PluginUtil。isIntentFromPlugin(intent)) {
//獲取外掛主題
int theme = PluginUtil。getTheme(mPluginManager。getHostContext(), intent);
if (theme != 0) {
Log。i(TAG, “resolve theme, current theme:” + activityInfo。theme + “ after :0x” + Integer。toHexString(theme));
activityInfo。theme = theme;
}
}
}
} catch (NoSuchFieldException e) {
e。printStackTrace();
} catch (IllegalAccessException e) {
e。printStackTrace();
}
}
return false;
}