Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> android插件開發-就是你了!啟動吧!插件的activity(一)

android插件開發-就是你了!啟動吧!插件的activity(一)

編輯:關於android開發

android插件開發-就是你了!啟動吧!插件的activity(一)


 

  通過之前的例子例子,我們學習了如何尋找hook點,並且做一些非常無聊的事情。比如是的粘貼板放一句無聊的句子,或者讓系統在啟動一個activity時打印一句話。這些看似無聊的事情其實都是為了本節做鋪墊。

  這一節會把之前的知識都貫穿起來——啟動插件中的activity,不過這篇博客比較長,所以我分開成為兩部分了第二部分

  啟動插件的activity還是非常難的一件事,因為在android中,所有的activity都必須在AndroidManifest.xml文件中聲明。如果沒有聲明的話,啟動它就會碰到下面的錯誤:

這裡寫圖片描述
傷透腦筋啊~

由於android的機制,我們無法啟動一個沒有在AndroidManifest.xml中沒有聲明的activity,並且我們不能像平時寫普通java代碼一樣,new一個Acticity對象出來就完事。因為android中的組件都是有生命的,不可以憑空產生,也不可以憑空消失,手動new出來的,它只是一個普通的對象,沒有任何用處。那麼我們是否可以,先在AndroidManifest.xml中聲明一個activity,然後我們插件中的activity都通過它借屍還魂,以此來運行呢?想法有點大膽,不過也沒辦法,因為我們現在能想到的就這麼多。

既然要借一個activity還魂,那麼肯定得了解activity的啟動原理啊,不然一切都真的是空談。通過我們之前的學習,我們注意到,當啟動一個activity時,Activity這個類中做這件事的其實是他的成員對象——mInstrumentation

這裡寫圖片描述

在這個函數裡面他最終是調用的是ActivityManagerNative.getDefault()的返回值來啟動一個activity
這裡寫圖片描述
ActivityManagerNative.getDefault返回的是一個Binder對象,他能夠使用ActivityManagerService的服務(以下簡稱AMS)。正如其名,它正是管理activity的服務,由他賦予activity生命!
這裡寫圖片描述
這裡寫圖片描述
通過一系列的遠程調用我們開始使用activity manager service的服務。其流程大概如下:
1:AMS調用ActivityStack的一系列方法來准備要啟動的Activity的相關信息。我們平時說的什麼任務棧啊都在這個類中有涉及
2:ActivityStack在完成一些准備工作後,通過ApplicationThread接口,遠程通知當前的ui線程,我要准備調度了~注意!ApplicationThread這個接口是在activity啟動另外一個activity的時候傳入Activity的
關於它的信息在這裡:
這裡寫圖片描述
3:ApplicationThread不執行真正的啟動操作,它通過調用ActivityManagerService.activityPaused接口進入到ActivityManagerService進程中,看看是否需要創建新的進程來啟動Activity。你大概可以感覺到了吧,ui線程通過ActivityManagerProxy與AMS”取得聯系”,而AMS呢,通過ApplicationThread與ui線程獲得聯系
4: 對於通過點擊應用程序圖標來啟動Activity的情景來說,AMS在這一步中,會調用startProcessLocked來創建一個新的進程,而對於通過在Activity內部調用startActivity來啟動新的Activity來說,這一步是不需要執行的,因為新的Activity就在原來的Activity所在的進程中進行啟動
5: AMS調用ApplicationThread.scheduleLaunchActivity接口,通知相應的進程執行啟動Activity的操作;
6: ApplicationThread把這個啟動Activity的操作轉發給ActivityThread,ActivityThread通過ClassLoader導入相應的Activity類,然後把它啟動起來。

以上內容有部分摘自老羅的博客
不過他看的android源碼有點老了,現在的源碼變化不小~

現在就開始分析吧

我們切入到AMS中看下:

1

這裡寫圖片描述
他調用了另外一個成員函數(這裡唠叨下,看到第一個參數沒?AMS通過他和我們的ui線程通信)

2

這裡寫圖片描述
這裡的AMS代碼被重構了一遍,這裡是要進入到ActivityStackSupervisor這個類中去處理了。從名字上我們很容易看出,這裡就是進行之前我們說的——讓ActivityStack做一些准備工作

3

這裡寫圖片描述

  final int startActivityMayWait(IApplicationThread caller, int callingUid,
            String callingPackage, Intent intent, String resolvedType,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int startFlags,
            ProfilerInfo profilerInfo, WaitResult outResult, Configuration config,
            Bundle options, int userId, IActivityContainer iContainer, TaskRecord inTask) {
        // Refuse possible leaked file descriptors
        if (intent != null && intent.hasFileDescriptors()) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }

        //查看下是否有component
        boolean componentSpecified = intent.getComponent() != null;

        // Don't modify the client's object!
        intent = new Intent(intent);

        // Collect information about the target of the Intent.
        ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags,
                profilerInfo, userId);

            ...

            int res = startActivityLocked(caller, intent, resolvedType, aInfo,
                    voiceSession, voiceInteractor, resultTo, resultWho,
                    requestCode, callingPid, callingUid, callingPackage,
                    realCallingPid, realCallingUid, startFlags, options,
                    componentSpecified, null, container, inTask);
            ...
            return res;
        }
    }

這裡有個非常重要的部分!

        //查看下是否有component
        boolean componentSpecified = intent.getComponent() != null;

我們平時可以有很多種方式啟動一個activity,比如隱式,顯式啟動
隱式:

        Intent intent = new Intent("your action");
        ...
        startActivity(intent);

顯式:

        Intent intent = new Intent(context, xxx.class);
        startActivity(intent);

我們這裡只考慮顯式。我們看下源碼:
這裡寫圖片描述
這個mComponent是一個ComponentName類型,他是系統用於區分組件的一個類:
這裡寫圖片描述
好像有那麼種感覺就是,AMS通過它區分要啟動的activity是什麼。回憶一下之前我介紹的activity啟動流程。ActivityStack准備好一切之後,會回到ui線程,然後UI線程再回頭問下AMS我是在當前進程啟動一個activity還是再創建一個進程啟動。這個過程是否有一種機制,讓AMS能夠快速識別這個Ui線程是哪個app的,畢竟手機裡不止一個應用嘛。
我們不急,繼續往下看。
之後的代碼就是解析出當前要啟動的activity信息:

  // Collect information about the target of the Intent.
        ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags,
                profilerInfo, userId);

切進去看下:

         @Override
3027     public ResolveInfo resolveIntent(Intent intent, String resolvedType,
3028             int flags, int userId) {
3029         if (!sUserManager.exists(userId)) return null;
3030         enforceCrossUserPermission(Binder.getCallingUid(), 
                 userId, false, false, "resolve intent");
3031         List query = queryIntentActivities(intent, resolvedType, flags, userId);
             //選擇出最優的activity
3032         return chooseBestActivity(intent, resolvedType, flags, query, userId);
3033     }

queryIntentActivities:

3349     @Override
3350     public List More ...queryIntentActivities(Intent intent,
3351             String resolvedType, int flags, int userId) {
3352         if (!sUserManager.exists(userId)) return Collections.emptyList();
3353         enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "query intent activities");
3354         ComponentName comp = intent.getComponent();
3355         if (comp == null) {
3356             if (intent.getSelector() != null) {
3357                 intent = intent.getSelector(); 
3358                 comp = intent.getComponent();
3359             }
3360         }
3361 
3362         if (comp != null) {
3363             final List list = new ArrayList(1);
3364             final ActivityInfo ai = getActivityInfo(comp, flags, userId);
3365             if (ai != null) {
3366                 final ResolveInfo ri = new ResolveInfo();
3367                 ri.activityInfo = ai;
3368                 list.add(ri);
3369             }
3370             return list;
3371         }
3372 
3373         // reader
3374         synchronized (mPackages) {
3375             final String pkgName = intent.getPackage();
3376             if (pkgName == null) {
3377                 List matchingFilters =
3378                         getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);
3379                 // Check for results that need to skip the current profile.
3380                 ResolveInfo resolveInfo  = querySkipCurrentProfileIntents(matchingFilters, intent,
3381                         resolvedType, flags, userId);
3382                 if (resolveInfo != null) {
3383                     List result = new ArrayList(1);
3384                     result.add(resolveInfo);
3385                     return result;
3386                 }
3387                 // Check for cross profile results.
3388                 resolveInfo = queryCrossProfileIntents(
3389                         matchingFilters, intent, resolvedType, flags, userId);
3390 
3391                 // Check for results in the current profile.
3392                 List result = mActivities.queryIntent(
3393                         intent, resolvedType, flags, userId);
3394                 if (resolveInfo != null) {
3395                     result.add(resolveInfo);
3396                     Collections.sort(result, mResolvePrioritySorter);
3397                 }
3398                 return result;
3399             }
3400             final PackageParser.Package pkg = mPackages.get(pkgName);
3401             if (pkg != null) {
3402                 return mActivities.queryIntentForPackage(intent, resolvedType, flags,
3403                         pkg.activities, userId);
3404             }
3405             return new ArrayList();
3406         }
3407     }

顯然是根據intent中提供的信息,檢索出最匹配的結果

4

之後調用startActivityLocked:

  final int startActivityLocked(IApplicationThread caller,
            Intent intent, String resolvedType, ActivityInfo aInfo,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode,
            int callingPid, int callingUid, String callingPackage,
            int realCallingPid, int realCallingUid, int startFlags, Bundle options,
            boolean componentSpecified, ActivityRecord[] outActivity, ActivityContainer container,
            TaskRecord inTask) {
        int err = ActivityManager.START_SUCCESS;

        //獲得調用者進程信息
        ProcessRecord callerApp = null;
        if (caller != null) {
            callerApp = mService.getRecordForAppLocked(caller);
            ...
        }

        if (err == ActivityManager.START_SUCCESS) {
              ...
        }

        //要啟動一個activity的activity的信息
        ActivityRecord sourceRecord = null;
        ActivityRecord resultRecord = null;
        if (resultTo != null) {
            sourceRecord = isInAnyStackLocked(resultTo);
            if (DEBUG_RESULTS) Slog.v(
                TAG, "Will send result to " + resultTo + " " + sourceRecord);
            if (sourceRecord != null) {
                if (requestCode >= 0 && !sourceRecord.finishing) {
                    resultRecord = sourceRecord;
                }
            }
        }

        //獲得intent的flags
        final int launchFlags = intent.getFlags();

        ...

        //要調用的activity信息
        ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
                intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
                requestCode, componentSpecified, this, container, options);
        ...

        err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
                startFlags, true, options, inTask);
        ...
        return err;
    }

我們看下ActivityRecord的ctor:
這裡寫圖片描述
有點長,我們再往下看:
這裡寫圖片描述
這裡寫圖片描述
可以得出的結論就是,AMS如何知道要啟動的activity是誰呢?就是通過intent,先解析Intent得到一些基本信息。然後根據這些結果生成activity recZ喎?http://www.Bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcmSjrLTmt8XU2rvutq/Vu8Dvw+ahozxiciAvPg0KztLDx7+0z8LP1Mq+xvS2r8qxSW50ZW50tcS5udTsuq/K/bDJo7o8YnIgLz4NCjxpbWcgYWx0PQ=="這裡寫圖片描述" src="http://www.bkjia.com/uploads/allimg/160417/0414292W6-14.png" title="\" />
Intent還提供了以另外一個方法:
這裡寫圖片描述
太酷了,我們完全可以這樣顯示啟動一個activity啊:

    Intent intent = new Intent();
    intent.setComponent(new ComponentName(MainActivity.this, Main2Activity.class));
    startActivity(intent);

那麼思路來了:我們是否可以先啟動一個無意義的activity(一下成為stub),它只是一個載體,然後我們hook AMS的startActivity方法,通過修改component為stub欺騙AMS,讓它誤以為要啟動的activity是stub,這個stub當然一定要在AndroidMenifest.xml注冊下,這樣一切都是合法的,之後我們再借屍還魂把那些資源轉移到我們插件的activity下,這樣,插件的activity就可以正常啟動,也成功獲得了生命周期,而AMS對於插件的activity的操作都被誤認為是對於stub的!沒瑕疵

talk is cheap, show u the code, just read the fucking code:

public class HookApplication extends Application {

    @TargetApi(Build.VERSION_CODES.KITKAT)
    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);

        try {
            //獲得ActivityManagerNative
            Class serviceManagerClz = Class.forName("android.app.ActivityManagerNative", false, getClassLoader());
            //獲得ActivityManagerNative.getDefault靜態方法
            Method getDefaultMethod = serviceManagerClz.getDeclaredMethod("getDefault");

            //獲得原始的IActivityManager對象
            Object rawIActivityManagerInterface = getDefaultMethod.invoke(null);
            //我們自己的Hook的對象
            Object hookIActivityManagerInterface = Proxy.newProxyInstance(
                    getClassLoader(),
                    new Class[]{Class.forName("android.app.IActivityManager", false, getClassLoader())},
                    new AMSHook(rawIActivityManagerInterface)
            );

            //反射ActivityManagerNative的gDefault域
            Field gDefaultField = serviceManagerClz.getDeclaredField("gDefault");
            gDefaultField.setAccessible(true);
            Object gDefaultObject = gDefaultField.get(null);

            //他的類型是Singleton
            Class singletonClz = Class.forName("android.util.Singleton", false, getClassLoader());

            //把他的mInstance域替換掉 成為我們自己的Hook對象
            Field mInstanceField = singletonClz.getDeclaredField("mInstance");
            mInstanceField.setAccessible(true);
            mInstanceField.set(gDefaultObject, hookIActivityManagerInterface);
        } catch (ClassNotFoundException | IllegalAccessException |
                NoSuchMethodException | InvocationTargetException | NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}
/**
 * Created by chan on 16/4/13.
 */
public class AMSHook implements InvocationHandler {

    private Object m_base;

    public AMSHook(Object base) {
        m_base = base;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        //攔截startActivity方法
        if ("startActivity".equals(method.getName())) {

            //查找原始的intent對象
            Intent raw = null;
            final int size = (args == null ? 0 : args.length);
            int i = 0;
            for (; i < size; ++i) {
                if (args[i] instanceof Intent) {
                    raw = (Intent) args[i];
                    break;
                }
            }

            //看下是否是啟動插件中的activity 下面的代碼會有解釋
            if (raw.getBooleanExtra(Constant.EXTRA_INVOKE_PLUGIN, false)) {

                //獲得原始的ComponentName
                ComponentName componentName = raw.getComponent();

                //創建一個新的Intent
                Intent intent = new Intent();

                //把Component替換為StubActivity的 這樣就不會被系統檢測到  啟動一個沒有在AndroidManifest.xml
                //中聲明的activity
                intent.setComponent(new ComponentName(componentName.getPackageName(),
                        StubActivity.class.getCanonicalName()));

                //保存原始的intent
                intent.putExtra(Constant.EXTRA_RAW_INTENT, raw);

                //替換為新的Intent
                args[i] = intent;
            }
        }

        //還是按往常一樣調用各種函數
        return method.invoke(m_base, args);
    }
}

一些工具類:

/**
 * Created by chan on 16/4/13.
 */
public interface Constant {
    String EXTRA_INVOKE_PLUGIN = "com.chan.hook.util.invoke_plugin";
    String EXTRA_RAW_INTENT = "com.chan.hook.util.raw_intent";
}
/**
 * Created by chan on 16/4/14.
 */
public class Utils {

    public static void invokePluginActivity(Activity activity, Class who) {
        Intent intent = new Intent(activity, who);
        intent.putExtra(Constant.EXTRA_INVOKE_PLUGIN, true);
        activity.startActivity(intent);
    }
}

AndroidManifest.xml:

  
        
            
                

                
            
        
        
            
            
        
        
        
    

使用方式:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.id_start).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Utils.invokePluginActivity(MainActivity.this, PluginActivity.class);
            }
        });
    }
}

效果:
這裡寫圖片描述

我們原本要啟動PluginActivity,但是借助StubActivity成功欺騙AMS獲取啟動一個activity所必須的資源。說明之前的思路都是正確的,下面就只剩正確的把AMS返回的資源給PluginActivity,讓PluginActivity啟動就行了。具體請參考第二部分的博客,我還會介紹上述代碼的原理~

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved