Why can browsers evoke App Activity?

Posted by postmanager on Fri, 21 Jan 2022 03:55:10 +0100

We do not actively declare the class of the Activity, so how does the system find the corresponding Activity for us? In fact, this is the same as the normal Activity startup process, except that the implementation of if / else is different.

The beginning of doubt

Have you ever thought about a question: where browser Open a web page in. There is a button on the web page that can be called by clicking App.

 

How can this effect be achieved? The browser is an app; Why can an app call up the pages of other apps?

When it comes to cross app page calls, can you think of a mechanism: Activity Implicit call?

Implicit start principle

When we need to call the pages of other app s, the API we use is implicit call.

For example, we have an app that declares such an Activity:

<activity android:name=".OtherActivity"
    android:screenOrientation="portrait">
    <intent-filter>
        <action android:name="mdove"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>
 

If other apps want to start the above Activity, just call the following:

val intent = Intent() 
intent.action = "mdove" 
startActivity(intent) 

We do not actively declare the class of the Activity, so how does the system find the corresponding Activity for us? In fact, this is the same as the normal Activity startup process, except that the implementation of if / else is different.

Next, let's review the startup process of Activity. In order to avoid falling into details, we only expand the "familiar" classes and call stacks, mainly string processes.

Cross process

First of all, we must be clear: whether it is implicit startup or display startup; Whether you start an Activity inside the App or an Activity outside the App, it is cross process. For example, in our above example, if an App wants to start the page of another App, it involves at least three processes.

Note that a mobile phone without root cannot see the process hatched by the system. That is why some code can't break points.

Students who have followed startActivity() should be familiar with the following call process. After following up several methods, they will find that they have entered a class called ActivityTread.

What are the characteristics of the ActivityTread class? There is a main function, which is our main thread.

Soon we can see the call of a common class: Instrumentation:

// Activity.java
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) {
    mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options);
    //Omit
}
 

Note that mminstrumentation#execstartactivity () has a yellow input parameter, which is the internal class ApplicationThread in ActivityThread.

What are the characteristics of the ApplicationThread class? It implements iaapplicationthread Stub, that is, aidl's "client callback for cross process calls".

In addition, another famous call will be seen in miminstrumentation #execstartactivity():

public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {
    //Omit
    ActivityManager.getService()
        .startActivity(whoThread, who.getBasePackageName(), intent,
                intent.resolveTypeIfNeeded(who.getContentResolver()),
                token, target != null ? target.mEmbeddedID : null,
                requestCode, 0, null, options);
    return null;
}
 

When we click getService(), we will see a red marked IActivityManager class.

It is not a java file, but aidl file.

So activitymanager In essence, getservice () returns the "server side of the process" interface instance, that is:

ActivityManagerService

public class ActivityManagerService extends IActivityManager.Stub

So the execution goes to the system_process. Omit the code details and take a look at the calling stack:

 

From the above debug screenshot, we can see that we have obtained the relevant information of our target activity at this time.

Here we simplify the source code of the target class and directly introduce the conclusion:

PackageManagerService

This class is equivalent to parsing all apk s in the mobile phone and constructing their information into memory, such as the following figure:

 

 

Small tips: in the mobile phone directory, / data / system / packages XML, you can see the path, process name, permission and other information of all apk s.

Start a new process

The premise of opening the target Activity is that the process of the target Activity is started. Therefore, the first time you want to open the target Activity, you need to start the process.

The code to start the process is in the method to start the Activity:

resumeTopActivityInnerLocked->startProcessLocked.

Here we introduce another famous class: ZygoteInit. Simply put, the App process will be started through ZygoteInit.

ApplicationThread

After the process starts, continue to return to the start process of the target Activity. Here is still a series of systems_ Process, and then iaapplicationthread enters the target process.

Notice that the iaapplicationthread callback to the ActivityThread again here.

class H extends Handler {
    //Omit
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case EXECUTE_TRANSACTION:
                final ClientTransaction transaction = (ClientTransaction) msg.obj;
                mTransactionExecutor.execute(transaction);
                //Omit
                break;
            case RELAUNCH_ACTIVITY:
                handleRelaunchActivityLocally((IBinder) msg.obj);
                break;
        }
        //Omit
    }
}

//Execute Callback
public void execute(ClientTransaction transaction) {
    final IBinder token = transaction.getActivityToken();
    executeCallbacks(transaction);
}
 

The implementation of CallBack here is the corresponding implementation of LaunchActivityItem#execute():

public void execute(ClientTransactionHandler client, IBinder token,
        PendingTransactionActions pendingActions) {
    ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
            mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
            mPendingResults, mPendingNewIntents, mIsForward,
            mProfilerInfo, client);
    client.handleLaunchActivity(r, pendingActions, null);
}
 

At this point, we go to ActivityThread#handleLaunchActivity(), which also goes to our daily life cycle. The call stack is as follows:

 

The call chain in the above screenshot implies the process of Activity instantiation (reflection):

public @NonNull Activity instantiateActivity(@NonNull ClassLoader cl, @NonNull String className, @Nullable Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {

    return (Activity) cl.loadClass(className).newInstance();

}
 

3

Browser startup principle

The reflow page in Hello is a standard, and the browser evokes another instance of the App.

Interactive process

html tags have a href attribute, such as: < a href = "..." >.

A common usage: < a href = "`` https://www.baidu.com ``">. That is, click to jump to Baidu.

Because this is the front-end tag, relying on the implementation of the browser and its kernel, jumping to a web page seems to be "natural" (otherwise called what browser).

Of course, the process here is basically the same as that of android interaction: declare the Activity to be started by implicit call; Then < a href = "" > pass in the corresponding protocol (scheme). For example:

Front page:

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<a href="mdove1://Haha "> start OtherActivity</a>
</body>
 

android statement:

<activity
    android:name=".OtherActivity"
    android:screenOrientation="portrait">
    <intent-filter>
        <data
            android:host="haha"
            android:scheme="mdove1" />
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.BROWSABLE" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>
 

Reasoning implementation

The browser can load scheme, which can be understood as the encapsulation of the browser kernel. So is it the browser kernel that wants android to support scheme parsing?

Obviously, it's impossible to make a mobile operating system and let the browser implement it. Isn't it a bit murderous.

Therefore, the probability can be guessed that it should be handled by the browser app in the mobile phone. Let's take a look at the browser based on this conjecture apk implementation.

Browser implementation

Based on / data / system / packages XML file, we can pull out the browser apk.

 

 

Then jadx decompile the browser Source code related to WebView in APK:


 

 

 

We can find that the processing of href comes from implicit jump, so everything is connected with the above process.

Epilogue

Leave a small question at the end: if I write a WebView to load a front-end page, can I jump implicitly?

Original link: https://mp.weixin.qq.com/s/vWZDgOB0oFSiQg305Qqwiw

Topics: Android Android Studio android-studio