Settings application details page & Android application installer - com.google.android.packageinstaller

Settings application details page

$ adb shell dumpsys activity | grep "mResumedActivity" --color=auto
    mResumedActivity: ActivityRecord{32c5bd u0 com.android.settings/.applications.InstalledAppDetails t28}

code

Main actions:

@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)

public static final String ACTION_APPLICATION_DETAILS_SETTINGS = "android.settings.APPLICATION_DETAILS_SETTINGS";

                Intent intent =  new Intent();
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                if (Build.VERSION.SDK_INT >= 9) {
                    intent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
                    intent.setData(Uri.fromParts("package", "指定应用包名, null));
                } else if (Build.VERSION.SDK_INT <= 8) {
                    intent.setAction(Intent.ACTION_VIEW);
                    intent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails");
                    intent.putExtra("com.android.settings.ApplicationPkgName", "指定应用的包名");
                }
                startActivity(intent);

Configuration file

         <!-- Still need a top-level activity for showing app details.  Aliasing
              trick is so the code that is now a fragment can still be called
              InstalledAppDetails. -->
         <activity android:name=".applications.InstalledAppDetailsTop"
                   android:label="@string/application_info_label"
                   android:exported="true" />
 
         <!-- Keep compatibility with old shortcuts. -->
         <activity-alias android:name=".applications.InstalledAppDetails"
                 android:label="@string/application_info_label"
                 android:exported="true"
                 android:targetActivity=".applications.InstalledAppDetailsTop">
             <intent-filter android:priority="1">
                 <action android:name="android.settings.APPLICATION_DETAILS_SETTINGS" />
                 <action android:name="android.intent.action.AUTO_REVOKE_PERMISSIONS" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <data android:scheme="package" />
             </intent-filter>
         </activity-alias>

Common application installation logic

packageUtils.installApp = function (path) {
    if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {//sd 卡已挂载
        var targetFile = new java.io.File(path);
        if (targetFile.exists()) {
            var intent = new android.content.Intent(android.content.Intent.ACTION_VIEW);
            intent.addCategory(android.content.Intent.CATEGORY_DEFAULT);
            intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.addFlags(android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION)
            intent.addFlags(android.content.Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
            intent.setDataAndType(getFileUri(context, targetFile), "application/vnd.android.package-archive");
            context.startActivity(intent);
        } else {
            console.log("文件不存在");
        }
    }
}

The main core is in  application/vnd.android.package-archive. This is the identifier for opening the .apk file.

Intent.setData

There are two ways to set up here

    public @NonNull Intent setData(@Nullable Uri data) {
        mData = data;
        mType = null;
        return this;
    }

    public @NonNull Intent setDataAndType(@Nullable Uri data, @Nullable String type) {
        mData = data;
        mType = type;
        return this;
    }

As for data and Uri, you can refer to  android.net.Uri parse(String uriString) |  android.content.parseUriInternal(String uri, @UriFlags int flags)

Regarding this mType, we often see this kind of menu in android phones using xx to play, using xxx browser, sharing to xxx, etc.

{".3gp", "video/3gpp"},  
{".apk", "application/vnd.android.package-archive"},  
{".asf", "video/x-ms-asf"},  
{".avi", "video/x-msvideo"},  
{".bin", "application/octet-stream"},  
{".bmp", "image/bmp"},  
{".c", "text/plain"},  
{".class", "application/octet-stream"},  
{".conf", "text/plain"},  
{".cpp", "text/plain"},  
{".doc", "application/msword"},  
{".exe", "application/octet-stream"},  
{".gif", "image/gif"},  
{".gtar", "application/x-gtar"},  
{".gz", "application/x-gzip"},  
{".h", "text/plain"},  
{".htm", "text/html"},  
{".html", "text/html"},  
{".jar", "application/java-archive"},  
{".java", "text/plain"},  
{".jpeg", "image/jpeg"},  
{".jpg", "image/jpeg"},  
{".js", "application/x-javascript"},  
{".log", "text/plain"},  
{".m3u", "audio/x-mpegurl"},  
{".m4a", "audio/mp4a-latm"},  
{".m4b", "audio/mp4a-latm"},  
{".m4p", "audio/mp4a-latm"},  
{".m4u", "video/vnd.mpegurl"},  
{".m4v", "video/x-m4v"},  
{".mov", "video/quicktime"},  
{".mp2", "audio/x-mpeg"},  
{".mp3", "audio/x-mpeg"},  
{".mp4", "video/mp4"},  
{".mpc", "application/vnd.mpohun.certificate"},  
{".mpe", "video/mpeg"},  
{".mpeg", "video/mpeg"},  
{".mpg", "video/mpeg"},  
{".mpg4", "video/mp4"},  
{".mpga", "audio/mpeg"},  
{".msg", "application/vnd.ms-outlook"},  
{".ogg", "audio/ogg"},  
{".pdf", "application/pdf"},  
{".png", "image/png"},  
{".pps", "application/vnd.ms-powerpoint"},  
{".ppt", "application/vnd.ms-powerpoint"},  
{".prop", "text/plain"},  
{".rar", "application/x-rar-compressed"},  
{".rc", "text/plain"},  
{".rmvb", "audio/x-pn-realaudio"},  
{".rtf", "application/rtf"},  
{".sh", "text/plain"},  
{".tar", "application/x-tar"},  
{".tgz", "application/x-compressed"},  
{".txt", "text/plain"},  
{".wav", "audio/x-wav"},  
{".wma", "audio/x-ms-wma"},  
{".wmv", "audio/x-ms-wmv"},  
{".wps", "application/vnd.ms-works"},  
{".xml", "text/xml"},  
{".xml", "text/plain"},  
{".z", "application/x-compress"},  
{".zip", "application/zip"},  
{"", "*/*"} 

It can be seen that when opening the .apk file  application/vnd.android.package-archive, this value is the mimeType attribute assigned to the data android tag.

frameworks/base/packages/PackageInstaller/AndroidManifest.xml

It can be seen that the Activity registered here has the data attribute <data android:mimeType="application/vnd.android.package-archive" /> 

        <activity android:name=".InstallStart"
                android:theme="@android:style/Theme.Translucent.NoTitleBar"
                android:exported="true"
                android:excludeFromRecents="true">
            <intent-filter android:priority="1">
                <action android:name="android.intent.action.VIEW" />
                <action android:name="android.intent.action.INSTALL_PACKAGE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="content" />
                <data android:mimeType="application/vnd.android.package-archive" />
            </intent-filter>
            <intent-filter android:priority="1">
                <action android:name="android.intent.action.INSTALL_PACKAGE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="package" />
                <data android:scheme="content" />
            </intent-filter>
            <intent-filter android:priority="1">
                <action android:name="android.content.pm.action.CONFIRM_INSTALL" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

At this point we know that the original  context.startActivity(intent) was pulled up by com.android.packageinstaller.InstallStart

frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java

static final String SCHEME_PACKAGE = "package";

static final String SCHEME_CONTENT = "content";

        Intent nextActivity = new Intent(intent);
        nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT| Intent.FLAG_GRANT_READ_URI_PERMISSION);

        // The the installation source as the nextActivity thinks this activity is the source, hence
        // set the originating UID and sourceInfo explicitly
        nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_PACKAGE, callingPackage);
        nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_ATTRIBUTION_TAG,callingAttributionTag);
        nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo);
        nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);

        if (isSessionInstall) {
            nextActivity.setClass(this, PackageInstallerActivity.class);
        } else {
            Uri packageUri = intent.getData();

            if (packageUri != null && packageUri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
                // [IMPORTANT] This path is deprecated, but should still work. Only necessary
                // features should be added.

                // Copy file to prevent it from being changed underneath this process
                nextActivity.setClass(this, InstallStaging.class);
            } else if (packageUri != null && packageUri.getScheme().equals(PackageInstallerActivity.SCHEME_PACKAGE)) {
                nextActivity.setClass(this, PackageInstallerActivity.class);
            } else {
                Intent result = new Intent();
                result.putExtra(Intent.EXTRA_INSTALL_RESULT,PackageManager.INSTALL_FAILED_INVALID_URI);
                setResult(RESULT_FIRST_USER, result);

                nextActivity = null;
            }
        }

        if (nextActivity != null) {
            startActivity(nextActivity);
        }
        finish();

It can be seen that packageUri.getScheme().equals(ContentResolver.SCHEME_CONTENT) is true; so here startActivity(nextActivity); opens PackageInstallerActivity

Guess you like

Origin blog.csdn.net/yuhui77268769/article/details/128150011