• Settings应用详情页面 & 安卓应用安装器 - com.google.android.packageinstaller


    Settings应用详情页面

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

    代码

    主要的action:

    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)

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

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

    配置文件

    1. <activity android:name=".applications.InstalledAppDetailsTop"
    2. android:label="@string/application_info_label"
    3. android:exported="true" />
    4. <activity-alias android:name=".applications.InstalledAppDetails"
    5. android:label="@string/application_info_label"
    6. android:exported="true"
    7. android:targetActivity=".applications.InstalledAppDetailsTop">
    8. <intent-filter android:priority="1">
    9. <action android:name="android.settings.APPLICATION_DETAILS_SETTINGS" />
    10. <action android:name="android.intent.action.AUTO_REVOKE_PERMISSIONS" />
    11. <category android:name="android.intent.category.DEFAULT" />
    12. <data android:scheme="package" />
    13. intent-filter>
    14. activity-alias>

    普通应用安装逻辑

    1. packageUtils.installApp = function (path) {
    2. if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {//sd 卡已挂载
    3. var targetFile = new java.io.File(path);
    4. if (targetFile.exists()) {
    5. var intent = new android.content.Intent(android.content.Intent.ACTION_VIEW);
    6. intent.addCategory(android.content.Intent.CATEGORY_DEFAULT);
    7. intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
    8. intent.addFlags(android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION)
    9. intent.addFlags(android.content.Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
    10. intent.setDataAndType(getFileUri(context, targetFile), "application/vnd.android.package-archive");
    11. context.startActivity(intent);
    12. } else {
    13. console.log("文件不存在");
    14. }
    15. }
    16. }

    主要核心在 application/vnd.android.package-archive 这是打开.apk文件的标识

    Intent.setData

    这里设置有两种方式

    1. public @NonNull Intent setData(@Nullable Uri data) {
    2. mData = data;
    3. mType = null;
    4. return this;
    5. }
    6. public @NonNull Intent setDataAndType(@Nullable Uri data, @Nullable String type) {
    7. mData = data;
    8. mType = type;
    9. return this;
    10. }

    至于data 和 Uri 可以 参考 android.net.Uri parse(String uriString) | android.content.parseUriInternal(String uri, @UriFlags int flags)

    关于这个 mType 就是 android手机中经常看到用xx播放,用xxx浏览器,分享到xxx等.这种菜单

    1. {".3gp", "video/3gpp"},
    2. {".apk", "application/vnd.android.package-archive"},
    3. {".asf", "video/x-ms-asf"},
    4. {".avi", "video/x-msvideo"},
    5. {".bin", "application/octet-stream"},
    6. {".bmp", "image/bmp"},
    7. {".c", "text/plain"},
    8. {".class", "application/octet-stream"},
    9. {".conf", "text/plain"},
    10. {".cpp", "text/plain"},
    11. {".doc", "application/msword"},
    12. {".exe", "application/octet-stream"},
    13. {".gif", "image/gif"},
    14. {".gtar", "application/x-gtar"},
    15. {".gz", "application/x-gzip"},
    16. {".h", "text/plain"},
    17. {".htm", "text/html"},
    18. {".html", "text/html"},
    19. {".jar", "application/java-archive"},
    20. {".java", "text/plain"},
    21. {".jpeg", "image/jpeg"},
    22. {".jpg", "image/jpeg"},
    23. {".js", "application/x-javascript"},
    24. {".log", "text/plain"},
    25. {".m3u", "audio/x-mpegurl"},
    26. {".m4a", "audio/mp4a-latm"},
    27. {".m4b", "audio/mp4a-latm"},
    28. {".m4p", "audio/mp4a-latm"},
    29. {".m4u", "video/vnd.mpegurl"},
    30. {".m4v", "video/x-m4v"},
    31. {".mov", "video/quicktime"},
    32. {".mp2", "audio/x-mpeg"},
    33. {".mp3", "audio/x-mpeg"},
    34. {".mp4", "video/mp4"},
    35. {".mpc", "application/vnd.mpohun.certificate"},
    36. {".mpe", "video/mpeg"},
    37. {".mpeg", "video/mpeg"},
    38. {".mpg", "video/mpeg"},
    39. {".mpg4", "video/mp4"},
    40. {".mpga", "audio/mpeg"},
    41. {".msg", "application/vnd.ms-outlook"},
    42. {".ogg", "audio/ogg"},
    43. {".pdf", "application/pdf"},
    44. {".png", "image/png"},
    45. {".pps", "application/vnd.ms-powerpoint"},
    46. {".ppt", "application/vnd.ms-powerpoint"},
    47. {".prop", "text/plain"},
    48. {".rar", "application/x-rar-compressed"},
    49. {".rc", "text/plain"},
    50. {".rmvb", "audio/x-pn-realaudio"},
    51. {".rtf", "application/rtf"},
    52. {".sh", "text/plain"},
    53. {".tar", "application/x-tar"},
    54. {".tgz", "application/x-compressed"},
    55. {".txt", "text/plain"},
    56. {".wav", "audio/x-wav"},
    57. {".wma", "audio/x-ms-wma"},
    58. {".wmv", "audio/x-ms-wmv"},
    59. {".wps", "application/vnd.ms-works"},
    60. {".xml", "text/xml"},
    61. {".xml", "text/plain"},
    62. {".z", "application/x-compress"},
    63. {".zip", "application/zip"},
    64. {"", "*/*"}

    可以看出打开.apk文件 application/vnd.android.package-archive 是这个这个值就是赋给 data android 标签的 mimeType属性

    frameworks/base/packages/PackageInstaller/AndroidManifest.xml

    可以看出这里注册的Activity 有data属性 

    1. <activity android:name=".InstallStart"
    2. android:theme="@android:style/Theme.Translucent.NoTitleBar"
    3. android:exported="true"
    4. android:excludeFromRecents="true">
    5. <intent-filter android:priority="1">
    6. <action android:name="android.intent.action.VIEW" />
    7. <action android:name="android.intent.action.INSTALL_PACKAGE" />
    8. <category android:name="android.intent.category.DEFAULT" />
    9. <data android:scheme="content" />
    10. <data android:mimeType="application/vnd.android.package-archive" />
    11. intent-filter>
    12. <intent-filter android:priority="1">
    13. <action android:name="android.intent.action.INSTALL_PACKAGE" />
    14. <category android:name="android.intent.category.DEFAULT" />
    15. <data android:scheme="package" />
    16. <data android:scheme="content" />
    17. intent-filter>
    18. <intent-filter android:priority="1">
    19. <action android:name="android.content.pm.action.CONFIRM_INSTALL" />
    20. <category android:name="android.intent.category.DEFAULT" />
    21. intent-filter>
    22. activity>

    到了这里我们知道了当初 context.startActivity(intent) 的拉起来的就是 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";

    1. Intent nextActivity = new Intent(intent);
    2. nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT| Intent.FLAG_GRANT_READ_URI_PERMISSION);
    3. // The the installation source as the nextActivity thinks this activity is the source, hence
    4. // set the originating UID and sourceInfo explicitly
    5. nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_PACKAGE, callingPackage);
    6. nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_ATTRIBUTION_TAG,callingAttributionTag);
    7. nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo);
    8. nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);
    9. if (isSessionInstall) {
    10. nextActivity.setClass(this, PackageInstallerActivity.class);
    11. } else {
    12. Uri packageUri = intent.getData();
    13. if (packageUri != null && packageUri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
    14. // [IMPORTANT] This path is deprecated, but should still work. Only necessary
    15. // features should be added.
    16. // Copy file to prevent it from being changed underneath this process
    17. nextActivity.setClass(this, InstallStaging.class);
    18. } else if (packageUri != null && packageUri.getScheme().equals(PackageInstallerActivity.SCHEME_PACKAGE)) {
    19. nextActivity.setClass(this, PackageInstallerActivity.class);
    20. } else {
    21. Intent result = new Intent();
    22. result.putExtra(Intent.EXTRA_INSTALL_RESULT,PackageManager.INSTALL_FAILED_INVALID_URI);
    23. setResult(RESULT_FIRST_USER, result);
    24. nextActivity = null;
    25. }
    26. }
    27. if (nextActivity != null) {
    28. startActivity(nextActivity);
    29. }
    30. finish();

    可以看出 packageUri.getScheme().equals(ContentResolver.SCHEME_CONTENT) 为 true;所以这里 startActivity(nextActivity); 打开的就是 PackageInstallerActivity

  • 相关阅读:
    方舟单机/管理员生物指令代码大全
    1103springcloud复习,eureka注册中心的使用
    windows中MySQL主从配置【第一篇】
    linux下常用命令查看端口占用
    宜家IKEA EDI项目案例
    JavaSE_多线程入门 线程安全 死锁 状态 通讯 线程池
    Redis笔记
    23软考备考已开始,网络工程师知识点速记~(4)
    HarmonyOS 非线性容器特性及使用场景
    【微服务】Spring条件注解从使用到源码分析详解
  • 原文地址:https://blog.csdn.net/yuhui77268769/article/details/128150011