Android plug-in development-you are now! Start it! Activity of the plug-in (1)

Source: Internet
Author: User

Android plug-in development-you are now! Start it! Activity of the plug-in (1)

 

Through the previous example, we learned how to find the hook points and do some very boring things. For example, you can place a boring sentence on the clipboard or let the system print a sentence when starting an activity. These seemingly boring things are actually paving the way for this section.

This section will introduce all previous knowledge-start the activity in the plug-in. However, this blog is a long one, so I separated it into the second part.

It is still very difficult to start a plug-in activity, because in android, all the activities must be declared in the AndroidManifest. xml file. If it is not declared, the following error will occur when it is started:


It's a headache ~

Because of the android mechanism, we cannot start an activity that is not declared in AndroidManifest. xml, and we can't get rid of a new Acticity object just like writing common java code. Because the components in android are all life-threatening, they cannot be generated out of thin air or disappear out of thin air. Manually new components are just a common object and are useless. Can we declare an activity in AndroidManifest. xml first, and then all the activities in our plug-in will be executed through it? The idea is a bit bold, but there is no way, because we can think of so much now.

Now that you want to use an activity to restore the soul, you must understand the startup principle of the activity. Otherwise, everything is really empty talk. Through our previous study, we noticed that when an activity is started, what the Activity class does is actually its member object-mInstrumentation.

In this function, he finally calls the return value of ActivityManagerNative. getDefault () to start an activity.

ActivityManagerNative. getDefault returns a Binder object that can use the ActivityManagerService Service (AMS ). Just like its name, it is the service used to manage the activity. It gives the activity life!


Through a series of remote calls, we start to use the service of activity manager service. The process is as follows:
1: AMS calls a series of ActivityStack methods to prepare information about the Activity to be started. What job stacks we usually talk about are involved in this class?
2: After completing some preparations, ActivityStack remotely notifies the current ui thread through the ApplicationThread interface. I want to prepare for scheduling ~ Note! The ApplicationThread interface is used to input the activity when the activity starts another Activity.
The information about it is here:

3: ApplicationThread does not execute a real Startup operation. It calls the ActivityManagerService. activityPaused interface to enter the ActivityManagerService process to see if a new process needs to be created to start the Activity. You can probably feel it. The ui thread uses ActivityManagerProxy to "get in touch with AMS", while the AMS gets in touch with the ui thread through ApplicationThread.
4: When you click the application icon to start the Activity, AMS calls startProcessLocked in this step to create a new process, this step does not need to be executed to start a new Activity by calling startActivity within the Activity, because the new Activity is started in the process where the original Activity is located.
5: AMS calls the ApplicationThread. scheduleLaunchActivity interface and notifies the corresponding process to start the Activity;
6: ApplicationThread forwards the Activity Startup operation to ActivityThread. ActivityThread imports the Activity class through ClassLoader and starts it.

Some of the above content is taken from Lao Luo's blog
However, the android source code he saw is a little old, and the current source code changes a lot ~

Start analysis now

Let's take a look At AMS:

1


He called another member function (which is the first parameter? AMS communicates with our ui thread through him)

2


Here the AMS code is restructured, and it is to be processed in the ActivityStackSupervisor class. From the name, we can easily see that this is what we mentioned earlier -- let ActivityStack do some preparation work.

3

Final int Invocation (IApplicationThread caller, int callingUid, String identifier, Intent intent, String identifier, callback voiceSession, response timeout, IBinder resul.pdf, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, waitResult outResult, Configuration config, Bundle options, int userId, IActivityContainer iContain Er, TaskRecord inTask) {// Refuse possible leaked file descriptors if (intent! = Null & intent. hasFileDescriptors () {throw new IllegalArgumentException ("File descriptors passed in Intent");} // check whether component boolean componentSpecified = intent exists. 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, guest, aInfo, voiceSession, callback, resulguid, resultWho, requestCode, callingPid, callingUid, callback, realCallingPid, realCallingUid, startFlags, options, callback, null, container, inTask );... return res ;}}

Here is a very important part!

// Check whether component boolean componentSpecified = intent. getComponent () exists ()! = Null;

We can start an activity in many ways, such as implicit and explicit startup.
Implicit:

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

Explicit:

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

Here we only consider explicit. Let's take a look at the source code:

This mComponent is a ComponentName type, which is a class used by the system to differentiate components:

It seems that AMS uses it to distinguish the activity to be started. Recall the previous activity startup process. After ActivityStack has everything ready, it will return to the ui thread, and then the UI thread will go back and ask if AMS is starting an activity in the current process or creating another process to start. Does this process have a mechanism that allows AMS to quickly identify which app the Ui thread belongs to? After all, there are more than one app in the mobile phone.
We are not in a rush to continue reading.
The following code parses the information of the activity to be started:

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

Switch in to see the following:

@ Override3027 public ResolveInfo resolveIntent (Intent intent, String resolvedType, 3028 int flags, int userId) {3029 if (! SUserManager. exists (userId) return null; 3030 enforcecrosuserpermission (Binder. getCallingUid (), userId, false, false, "resolve intent"); 3031 List
  
   
Query = queryIntentActivities (intent, resolvedType, flags, userId); // select the optimal activity3032 return chooseBestActivity (intent, resolvedType, flags, query, userId); 3033}
  

QueryIntentActivities:

3349     @Override3350     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         // reader3374         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 }
         
        
       
      
     
    
   
  

Apparently, the most matched result is retrieved based on the information provided in intent.

4

Call startActivityLocked:

Final int startActivityLocked (IApplicationThread caller, Intent intent, String success, ActivityInfo aInfo, invalid voiceSession, response failed, IBinder resul.pdf, String resultWho, int requestCode, int callingPid, int callingUid, String success, int realCallingPid, int realCallingUid, int startFlags, Bundle options, boolean componentSpecified, Activit YRecord [] outActivity, ActivityContainer container, TaskRecord inTask) {int err = ActivityManager. START_SUCCESS; // obtain the caller Process Information ProcessRecord callerApp = null; if (caller! = Null) {callerApp = mService. getRecordForAppLocked (caller );...} if (err = ActivityManager. START_SUCCESS ){...} // ActivityRecord sourceRecord = null; ActivityRecord resultRecord = null; if (resuliterator! = Null) {sourceRecord = isInAnyStackLocked (resullocked); if (DEBUG_RESULTS) Slog. v (TAG, "Will send result to" + resul.pdf + "" + sourceRecord); if (sourceRecord! = Null) {if (requestCode> = 0 &&! SourceRecord. finishing) {resultRecord = sourceRecord ;}}// obtain the intent flags final int launchFlags = intent. getFlags ();... // ActivityRecord r = new ActivityRecord (mService, callerApp, callingUid, callingPackage, intent, resolvedType, aInfo, mService) of the activity to be called. mConfiguration, resultRecord, resultWho, requestCode, componentSpecified, this, container, options );... err = startActivityUncheckedLocked (r, sourceRecord, voiceSession, voiceInteractor, startFlags, true, options, inTask );... return err ;}

Let's take a look at ActivityRecord ctor:

A little long. Let's look at it again:


It can be concluded that how does AMS know who the activity is to be started? It is through intent, first parse Intent to get some basic information. Then, based on these results, the activity recZ progress? Http://www.bkjia.com/kf/ware/vc/ "target =" _ blank "class =" keylink "> vcmSjrLTmt8XU2rvutq/Vu8Dvw + fill + 0z8LP1Mq + fill/K/fill =" here write picture description "src =" http://www.bkjia.com/uploads/allimg/160417/0414292W6-14.png" title = "\"/>
Intent also provides another method:

It's so cool that we can start an activity like this:

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

Now let's get started: Can we start a meaningless activity (stub) first? It is just a carrier, and then we hook the startActivity method of AMS, modify component to stub to spoof AMS and make it mistakenly think that the activity to be started is stub. Of course, this stub must be in AndroidMenifest. in xml registration, everything is legal, and then we will transfer those resources to the activity of our plug-in, so that the activity of the plug-in can be started normally, the lifecycle is also successfully obtained, and the operations of AMS on the activity of the plug-in are mistaken for stub! Flawless

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 {// get ActivityManagerNative Class
  ServiceManagerClz = Class. forName ("android. app. activityManagerNative ", false, getClassLoader (); // obtain ActivityManagerNative. getDefault static Method getDefaultMethod = serviceManagerClz. getDeclaredMethod ("getDefault"); // obtain the original IActivityManager Object rawIActivityManagerInterface = getDefaultMethod. invoke (null); // our own Hook Object hookIActivityManagerInterface = Proxy. newProxyInstance (getClassLoader (), new Class [] {Class. forName ("android. app. IActivityManager ", false, getClassLoader ()}, new AMSHook (rawIActivityManagerInterface); // reflect the gDefault Field gdefafield Field = serviceManagerClz of ActivityManagerNative. getDeclaredField ("gDefault"); gDefaultField. setAccessible (true); Object gDefaultObject = gDefaultField. get (null); // Its type is Singleton Class
  SingletonClz = Class. forName ("android. util. singleton ", false, getClassLoader (); // replace his mInstance Field with our own Hook object 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 {// intercept the startActivity method if ("startActivity ". equals (method. getName () {// find the original intent object 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;} // check whether the code below the activity in the startup plug-in will explain if (raw. getBooleanExtra (Constant. EXTRA_INVOKE_PLUGIN, false) {// obtain the original ComponentName componentName = raw. getComponent (); // create a new Intent intent = new Intent (); // replace Component with StubActivity, so that the system will not detect the startup of a non-AndroidManifest. activity intent declared in xml. setComponent (new ComponentName (componentName. getPackageName (), StubActivity. class. getCanonicalName (); // Save the original intent. putExtra (Constant. EXTRA_RAW_INTENT, raw); // replace it with the new Intent args [I] = intent ;}// call the return method functions as usual. invoke (m_base, args );}}

Some tools:

/** * 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:

                      
                                  
               
   
                  
              
              
          
                      

Usage:

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);            }        });    }}

Effect:

We originally wanted to start PluginActivity, but we had to use StubActivity to successfully cheat AMS to get the resources necessary to start an activity. It indicates that the previous ideas are correct. In the following example, only resources returned by AMS are correctly assigned to PluginActivity, so that PluginActivity can be started. For details, refer to the blog in the second part. I will also introduce the principles of the above Code ~

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.