[toc]
基于 Android7.1.1 分析 PackageManagerService 的逻辑,Android 版本虽然会不断更替,但是代码结构和思想史
0 综述 前面总结了通过 pm install 的方式来安装一个 apk,但是这种方式用户是不经常使用的,用户使用的安装途径主要如下:
通过应用商店下载 apk,进行安装;
将 apk 文件移动到关键管理器,点击触发安装;
这里我们来看看第二种情况!
对于第二种中情况,当我们触发安装的时候,实际上是发送了一个 intent,这个 intent 中携带了 apk 的文件路径等参数,在 sample 给出了如下的启动安装的方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 private OnClickListener mUnknownSourceListener = new OnClickListener() { public void onClick (View v) { Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE); intent.setData(Uri.fromFile(prepareApk("HelloActivity.apk" ))); startActivity(intent); } }; private OnClickListener mMySourceListener = new OnClickListener() { public void onClick (View v) { Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE); intent.setData(Uri.fromFile(prepareApk("HelloActivity.apk" ))); intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true ); intent.putExtra(Intent.EXTRA_RETURN_RESULT, true ); intent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, getApplicationInfo().packageName); startActivityForResult(intent, REQUEST_INSTALL); } }; private OnClickListener mReplaceListener = new OnClickListener() { public void onClick (View v) { Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE); intent.setData(Uri.fromFile(prepareApk("HelloActivity.apk" ))); intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true ); intent.putExtra(Intent.EXTRA_RETURN_RESULT, true ); intent.putExtra(Intent.EXTRA_ALLOW_REPLACE, true ); intent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, getApplicationInfo().packageName); startActivityForResult(intent, REQUEST_INSTALL); } };
那谁来接收这个 intent 呢?显而易见,PacakgeInstaller,我们去其说明书中看看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <activity android:name =".PackageInstallerActivity" android:configChanges ="orientation|keyboardHidden|screenSize" android:excludeFromRecents ="true" > <intent-filter > <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 ="file" /> <data android:scheme ="content" /> <data android:mimeType ="application/vnd.android.package-archive" /> </intent-filter > <intent-filter > <action android:name ="android.intent.action.INSTALL_PACKAGE" /> <category android:name ="android.intent.category.DEFAULT" /> <data android:scheme ="file" /> <data android:scheme ="package" /> <data android:scheme ="content" /> </intent-filter > <intent-filter > <action android:name ="android.content.pm.action.CONFIRM_PERMISSIONS" /> <category android:name ="android.intent.category.DEFAULT" /> </intent-filter > </activity >
我们看到 PackageInstallerActivity 接收了 android.intent.action.INSTALL_PACKAGE action!
下面我们来分析下整个流程:
1 PackageInstallerActivity.onCreate 这里我们进入 PackageInstallerActivity,看看其做了什么处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 public class PackageInstallerActivity extends Activity implements OnCancelListener , OnClickListener { ... ... ... @Override protected void onCreate (Bundle icicle) { super .onCreate(icicle); mPm = getPackageManager(); mInstaller = mPm.getPackageInstaller(); mUserManager = (UserManager) getSystemService(Context.USER_SERVICE); final Intent intent = getIntent(); mOriginatingUid = getOriginatingUid(intent); final Uri packageUri; if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) { final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1 ); final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId); if (info == null || !info.sealed || info.resolvedBaseCodePath == null ) { Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring" ); finish(); return ; } mSessionId = sessionId; packageUri = Uri.fromFile(new File(info.resolvedBaseCodePath)); mOriginatingURI = null ; mReferrerURI = null ; } else { mSessionId = -1 ; packageUri = intent.getData(); mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI); mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER); } if (packageUri == null ) { Log.w(TAG, "Unspecified source" ); setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI); finish(); return ; } if (DeviceUtils.isWear(this )) { showDialogInner(DLG_NOT_SUPPORTED_ON_WEAR); return ; } setContentView(R.layout.install_start); mInstallConfirm = findViewById(R.id.install_confirm_panel); mInstallConfirm.setVisibility(View.INVISIBLE); mOk = (Button)findViewById(R.id.ok_button); mCancel = (Button)findViewById(R.id.cancel_button); mOk.setOnClickListener(this ); mCancel.setOnClickListener(this ); boolean wasSetUp = processPackageUri(packageUri); if (!wasSetUp) { return ; } checkIfAllowedAndInitiateInstall(false ); } ... ... ... }
我们看到 PackageInstallerActivity 方法实现了 OnCancelListener, OnClickListener,用于响应界面事件。。。
1.1 processPackageUri 解析 Uri 被为要安装的 apk 设置合适的 installer,如果返回 true,表示设置成功了!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 private boolean processPackageUri (final Uri packageUri) { mPackageURI = packageUri; final String scheme = packageUri.getScheme(); final PackageUtil.AppSnippet as; switch (scheme) { case SCHEME_PACKAGE: { try { mPkgInfo = mPm.getPackageInfo(packageUri.getSchemeSpecificPart(), PackageManager.GET_PERMISSIONS | PackageManager.GET_UNINSTALLED_PACKAGES); } catch (NameNotFoundException e) { } if (mPkgInfo == null ) { Log.w(TAG, "Requested package " + packageUri.getScheme() + " not available. Discontinuing installation" ); showDialogInner(DLG_PACKAGE_ERROR); setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK); return false ; } as = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo), mPm.getApplicationIcon(mPkgInfo.applicationInfo)); } break ; case SCHEME_FILE: { File sourceFile = new File(packageUri.getPath()); PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile); if (parsed == null ) { Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation" ); showDialogInner(DLG_PACKAGE_ERROR); setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK); return false ; } mPkgInfo = PackageParser.generatePackageInfo(parsed, null , PackageManager.GET_PERMISSIONS, 0 , 0 , null , new PackageUserState()); as = PackageUtil.getAppSnippet(this , mPkgInfo.applicationInfo, sourceFile); } break ; case SCHEME_CONTENT: { mStagingAsynTask = new StagingAsyncTask(); mStagingAsynTask.execute(packageUri); return false ; } default : { Log.w(TAG, "Unsupported scheme " + scheme); setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI); clearCachedApkIfNeededAndFinish(); return false ; } } PackageUtil.initSnippetForNewApp(this , as, R.id.app_snippet); return true ; }
对于安装文件管理器的 apk 的方式,其 scheme 值为 file,所以这里
对于 package 的解析流程,这里就不再多说了;
而 generatePackageInfo 方法的逻辑也很简单,flags 可以通过设置指定的位来获取特定的数据!
1.1.1 PackageUtil.getPackageInfo 1 2 3 4 5 6 7 8 public static PackageParser.Package getPackageInfo (File sourceFile) { final PackageParser parser = new PackageParser(); try { return parser.parsePackage(sourceFile, 0 ); } catch (PackageParserException e) { return null ; } }
1.1.2 PackageParser.generatePackageInfo 将解析到的 Package 封装为指定的 PackageInfo 对象,除了基本的信息之外,还有通过 flags 指定的额外信息!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 public static PackageInfo generatePackageInfo (PackageParser.Package p, int gids[], int flags, long firstInstallTime, long lastUpdateTime, Set<String> grantedPermissions, PackageUserState state, int userId) { if (!checkUseInstalledOrHidden(flags, state) || !p.isMatch(flags)) { return null ; } PackageInfo pi = new PackageInfo(); pi.packageName = p.packageName; pi.splitNames = p.splitNames; pi.versionCode = p.mVersionCode; pi.baseRevisionCode = p.baseRevisionCode; pi.splitRevisionCodes = p.splitRevisionCodes; pi.versionName = p.mVersionName; pi.sharedUserId = p.mSharedUserId; pi.sharedUserLabel = p.mSharedUserLabel; pi.applicationInfo = generateApplicationInfo(p, flags, state, userId); pi.installLocation = p.installLocation; pi.coreApp = p.coreApp; if ((pi.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0 || (pi.applicationInfo.flags&ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0 ) { pi.requiredForAllUsers = p.mRequiredForAllUsers; } pi.restrictedAccountType = p.mRestrictedAccountType; pi.requiredAccountType = p.mRequiredAccountType; pi.overlayTarget = p.mOverlayTarget; pi.firstInstallTime = firstInstallTime; pi.lastUpdateTime = lastUpdateTime; ... ... ... ... if ((flags&PackageManager.GET_PERMISSIONS) != 0 ) { int N = p.permissions.size(); if (N > 0 ) { pi.permissions = new PermissionInfo[N]; for (int i=0 ; i<N; i++) { pi.permissions[i] = generatePermissionInfo(p.permissions.get(i), flags); } } N = p.requestedPermissions.size(); if (N > 0 ) { pi.requestedPermissions = new String[N]; pi.requestedPermissionsFlags = new int [N]; for (int i=0 ; i<N; i++) { final String perm = p.requestedPermissions.get(i); pi.requestedPermissions[i] = perm; pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED; if (grantedPermissions != null && grantedPermissions.contains(perm)) { pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_GRANTED; } } } } ... ... ... return pi; }
这里涉及到了几个标志位:
1 2 3 4 public static final int REQUESTED_PERMISSION_REQUIRED = 1 <<0 ;public static final int REQUESTED_PERMISSION_GRANTED = 1 <<1 ;
这两个标签都是在 generatePackageInfo 方法中,配合 GET_PERMISSIONS 方法使用!
1.1.3 PackageUtil.getAppSnippet 这个方法用于加载应用的 label 和 icon:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public static AppSnippet getAppSnippet ( Activity pContext, ApplicationInfo appInfo, File sourceFile) { final String archiveFilePath = sourceFile.getAbsolutePath(); Resources pRes = pContext.getResources(); AssetManager assmgr = new AssetManager(); assmgr.addAssetPath(archiveFilePath); Resources res = new Resources(assmgr, pRes.getDisplayMetrics(), pRes.getConfiguration()); CharSequence label = null ; if (appInfo.labelRes != 0 ) { try { label = res.getText(appInfo.labelRes); } catch (Resources.NotFoundException e) { } } if (label == null ) { label = (appInfo.nonLocalizedLabel != null ) ? appInfo.nonLocalizedLabel : appInfo.packageName; } Drawable icon = null ; if (appInfo.icon != 0 ) { try { icon = res.getDrawable(appInfo.icon); } catch (Resources.NotFoundException e) { } } if (icon == null ) { icon = pContext.getPackageManager().getDefaultActivityIcon(); } return new PackageUtil.AppSnippet(label, icon); }
1.1.2.1 new AppSnippet AppSnippet 用于保存 apk 的 label 和 icon:
1 2 3 4 5 6 7 8 static public class AppSnippet { CharSequence label; Drawable icon; public AppSnippet (CharSequence label, Drawable icon) { this .label = label; this .icon = icon; } }
1.1.4 PackageUtil.initSnippetForNewApp 根据返回的 AppSnippet,初始化界面,显示应用的名称和 label:1 2 3 4 5 6 7 public static View initSnippetForNewApp (Activity pContext, AppSnippet as, int snippetId) { View appSnippet = pContext.findViewById(snippetId); ((ImageView)appSnippet.findViewById(R.id.app_icon)).setImageDrawable(as.icon); ((TextView)appSnippet.findViewById(R.id.app_name)).setText(as.label); return appSnippet; }
1.2 checkIfAllowedAndInitiateInstall 用于判断是否允许安装,如果允许安装,那就进行初始化;否则会弹出提示框,参数 ignoreUnknownSourcesSettings 表示是否忽视未知来源安装,这里我们传入的是 false;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 private void checkIfAllowedAndInitiateInstall (boolean ignoreUnknownSourcesSettings) { final boolean requestFromUnknownSource = isInstallRequestFromUnknownSource(getIntent()); if (!requestFromUnknownSource) { initiateInstall(); } final boolean isManagedProfile = mUserManager.isManagedProfile(); if (isUnknownSourcesDisallowed()) { if ((mUserManager.getUserRestrictionSource(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, Process.myUserHandle()) & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0 ) { if (ignoreUnknownSourcesSettings) { initiateInstall(); } else { showDialogInner(DLG_UNKNOWN_SOURCES); } } else { startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS)); clearCachedApkIfNeededAndFinish(); } } else if (!isUnknownSourcesEnabled() && isManagedProfile) { showDialogInner(DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES); } else if (!isUnknownSourcesEnabled()) { if (ignoreUnknownSourcesSettings) { initiateInstall(); } else { showDialogInner(DLG_UNKNOWN_SOURCES); } } else { initiateInstall(); } }
1.2.1 isInstallRequestFromUnknownSource 判断本次安装是否来自未知来源:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 private boolean isInstallRequestFromUnknownSource (Intent intent) { String callerPackage = getCallingPackage(); if (callerPackage != null && intent.getBooleanExtra( Intent.EXTRA_NOT_UNKNOWN_SOURCE, false )) { try { mSourceInfo = mPm.getApplicationInfo(callerPackage, 0 ); if (mSourceInfo != null ) { if ((mSourceInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0 ) { return false ; } } } catch (NameNotFoundException e) { } } return true ; }
1.2.2 isUnknownSourcesDisallowed 判断下当前设备用户是否限制来自未知来源的安装,也就是是否设置了 DISALLOW_INSTALL_UNKNOWN_SOURCES 用户限制:
1 2 3 private boolean isUnknownSourcesDisallowed () { return mUserManager.hasUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES); }
1.2.3 isUnknownSourcesEnabled 判断用户是否在设置中打开了位置来源限制:
1 2 3 4 private boolean isUnknownSourcesEnabled () { return Settings.Secure.getInt(getContentResolver(), Settings.Secure.INSTALL_NON_MARKET_APPS, 0 ) > 0 ; }
1.2.6 initiateInstall 初始化安装:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 private void initiateInstall () { String pkgName = mPkgInfo.packageName; String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName }); if (oldName != null && oldName.length > 0 && oldName[0 ] != null ) { pkgName = oldName[0 ]; mPkgInfo.packageName = pkgName; mPkgInfo.applicationInfo.packageName = pkgName; } try { mAppInfo = mPm.getApplicationInfo(pkgName, PackageManager.GET_UNINSTALLED_PACKAGES); if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0 ) { mAppInfo = null ; } } catch (NameNotFoundException e) { mAppInfo = null ; } startInstallConfirm(); }
2 PackageInstallerActivity.startInstallConfirm 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 private void startInstallConfirm () { ((TextView) findViewById(R.id.install_confirm_question)) .setText(R.string.install_confirm_question); findViewById(R.id.spacer).setVisibility(View.GONE); TabHost tabHost = (TabHost)findViewById(android.R.id.tabhost); tabHost.setup(); tabHost.setVisibility(View.VISIBLE); ViewPager viewPager = (ViewPager)findViewById(R.id.pager); TabsAdapter adapter = new TabsAdapter(this , tabHost, viewPager); boolean supportsRuntimePermissions = mPkgInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M; boolean permVisible = false ; mScrollView = null ; mOkCanInstall = false ; int msg = 0 ; AppSecurityPermissions perms = new AppSecurityPermissions(this , mPkgInfo); final int N = perms.getPermissionCount(AppSecurityPermissions.WHICH_ALL); if (mAppInfo != null ) { msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 ? R.string.install_confirm_question_update_system : R.string.install_confirm_question_update; mScrollView = new CaffeinatedScrollView(this ); mScrollView.setFillViewport(true ); boolean newPermissionsFound = false ; if (!supportsRuntimePermissions) { newPermissionsFound = (perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0 ); if (newPermissionsFound) { permVisible = true ; mScrollView.addView(perms.getPermissionsView( AppSecurityPermissions.WHICH_NEW)); } } if (!supportsRuntimePermissions && !newPermissionsFound) { LayoutInflater inflater = (LayoutInflater)getSystemService( Context.LAYOUT_INFLATER_SERVICE); TextView label = (TextView)inflater.inflate(R.layout.label, null ); label.setText(R.string.no_new_perms); mScrollView.addView(label); } adapter.addTab(tabHost.newTabSpec(TAB_ID_NEW).setIndicator( getText(R.string.newPerms)), mScrollView); } else { findViewById(R.id.tabscontainer).setVisibility(View.GONE); findViewById(R.id.spacer).setVisibility(View.VISIBLE); } if (!supportsRuntimePermissions && N > 0 ) { permVisible = true ; LayoutInflater inflater = (LayoutInflater)getSystemService( Context.LAYOUT_INFLATER_SERVICE); View root = inflater.inflate(R.layout.permissions_list, null ); if (mScrollView == null ) { mScrollView = (CaffeinatedScrollView)root.findViewById(R.id.scrollview); } ((ViewGroup)root.findViewById(R.id.permission_list)).addView( perms.getPermissionsView(AppSecurityPermissions.WHICH_ALL)); adapter.addTab(tabHost.newTabSpec(TAB_ID_ALL).setIndicator( getText(R.string.allPerms)), root); } if (!permVisible) { if (mAppInfo != null ) { msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 ? R.string.install_confirm_question_update_system_no_perms : R.string.install_confirm_question_update_no_perms; findViewById(R.id.spacer).setVisibility(View.VISIBLE); } else { msg = R.string.install_confirm_question_no_perms; } tabHost.setVisibility(View.INVISIBLE); mScrollView = null ; } if (msg != 0 ) { ((TextView)findViewById(R.id.install_confirm_question)).setText(msg); } mInstallConfirm.setVisibility(View.VISIBLE); mOk.setEnabled(true ); if (mScrollView == null ) { mOk.setText(R.string.install); mOkCanInstall = true ; } else { mScrollView.setFullScrollAction(new Runnable() { @Override public void run () { mOk.setText(R.string.install); mOkCanInstall = true ; } }); } }
不多说了,继续看!
2.1 new AppSecurityPermissions AppSecurityPermissions 有多个构造器,这里调用了 AppSecurityPermissions 的双参构造函数,同时 AppSecurityPermissions 内部也有很多的成员变量,我们来分析下:
这里的 PackageInfo info 表示的是本次要安装的应用程序的 PackageInfo 实例!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public class AppSecurityPermissions { ... ... ... public AppSecurityPermissions (Context context, PackageInfo info) { this (context); Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>(); if (info == null ) { return ; } mPackageName = info.packageName; PackageInfo installedPkgInfo = null ; if (info.requestedPermissions != null ) { try { installedPkgInfo = mPm.getPackageInfo(info.packageName, PackageManager.GET_PERMISSIONS); } catch (NameNotFoundException e) { } extractPerms(info, permSet, installedPkgInfo); } if (info.sharedUserId != null ) { int sharedUid; try { sharedUid = mPm.getUidForSharedUser(info.sharedUserId); getAllUsedPermissions(sharedUid, permSet); } catch (NameNotFoundException e) { Log.w(TAG, "Couldn't retrieve shared user id for: " + info.packageName); } } mPermsList.addAll(permSet); setPermissions(mPermsList); } ... ... ... }
AppSecurityPermissions 中有如下重要变量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private final Map<String, MyPermissionGroupInfo> mPermGroups = new HashMap<String, MyPermissionGroupInfo>(); private final List<MyPermissionGroupInfo> mPermGroupsList = new ArrayList<MyPermissionGroupInfo>(); private final PermissionGroupInfoComparator mPermGroupComparator = new PermissionGroupInfoComparator(); private final PermissionInfoComparator mPermComparator = new PermissionInfoComparator();private final List<MyPermissionInfo> mPermsList = new ArrayList<MyPermissionInfo>();private final CharSequence mNewPermPrefix;private String mPackageName;
我们后面分析的时候会遇到!
这里会比较本次安装的应用和已经安装的旧应用(如果有)的权限,做相应处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 private void extractPerms (PackageInfo info, Set<MyPermissionInfo> permSet, PackageInfo installedPkgInfo) { String[] strList = info.requestedPermissions; int [] flagsList = info.requestedPermissionsFlags; if ((strList == null ) || (strList.length == 0 )) { return ; } for (int i=0 ; i<strList.length; i++) { String permName = strList[i]; try { PermissionInfo tmpPermInfo = mPm.getPermissionInfo(permName, 0 ); if (tmpPermInfo == null ) { continue ; } int existingIndex = -1 ; if (installedPkgInfo != null && installedPkgInfo.requestedPermissions != null ) { for (int j=0 ; j<installedPkgInfo.requestedPermissions.length; j++) { if (permName.equals(installedPkgInfo.requestedPermissions[j])) { existingIndex = j; break ; } } } final int existingFlags = existingIndex >= 0 ? installedPkgInfo.requestedPermissionsFlags[existingIndex] : 0 ; if (!isDisplayablePermission(tmpPermInfo, flagsList[i], existingFlags)) { continue ; } final String origGroupName = tmpPermInfo.group; String groupName = origGroupName; if (groupName == null ) { groupName = tmpPermInfo.packageName; tmpPermInfo.group = groupName; } MyPermissionGroupInfo group = mPermGroups.get(groupName); if (group == null ) { PermissionGroupInfo grp = null ; if (origGroupName != null ) { grp = mPm.getPermissionGroupInfo(origGroupName, 0 ); } if (grp != null ) { group = new MyPermissionGroupInfo(grp); } else { tmpPermInfo.group = tmpPermInfo.packageName; group = mPermGroups.get(tmpPermInfo.group); if (group == null ) { group = new MyPermissionGroupInfo(tmpPermInfo); } group = new MyPermissionGroupInfo(tmpPermInfo); } mPermGroups.put(tmpPermInfo.group, group); } final boolean newPerm = installedPkgInfo != null && (existingFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0 ; MyPermissionInfo myPerm = new MyPermissionInfo(tmpPermInfo); myPerm.mNewReqFlags = flagsList[i]; myPerm.mExistingReqFlags = existingFlags; myPerm.mNew = newPerm; permSet.add(myPerm); } catch (NameNotFoundException e) { Log.i(TAG, "Ignoring unknown permission:" +permName); } } }
不多说了。。
2.1.1.1 AppSecurityPermissions.isDisplayablePermission isDisplayablePermission 用于判断哪些权限会在界面中显示,那些不显示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 private boolean isDisplayablePermission (PermissionInfo pInfo, int newReqFlags, int existingReqFlags) { final int base = pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE; final boolean isNormal = (base == PermissionInfo.PROTECTION_NORMAL); if (isNormal) { return false ; } final boolean isDangerous = (base == PermissionInfo.PROTECTION_DANGEROUS) || ((pInfo.protectionLevel&PermissionInfo.PROTECTION_FLAG_PRE23) != 0 ); final boolean isRequired = ((newReqFlags&PackageInfo.REQUESTED_PERMISSION_REQUIRED) != 0 ); final boolean isDevelopment = ((pInfo.protectionLevel&PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0 ); final boolean wasGranted = ((existingReqFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0 ); final boolean isGranted = ((newReqFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0 ); if (isDangerous && (isRequired || wasGranted || isGranted)) { return true ; } if (isDevelopment && wasGranted) { if (localLOGV) Log.i(TAG, "Special perm " + pInfo.name + ": protlevel=0x" + Integer.toHexString(pInfo.protectionLevel)); return true ; } return false ; }
不多说了!
2.1.1.2 new MyPermissionGroupInfo 创建一个权限组对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 static class MyPermissionGroupInfo extends PermissionGroupInfo { CharSequence mLabel; final ArrayList<MyPermissionInfo> mNewPermissions = new ArrayList<MyPermissionInfo>(); final ArrayList<MyPermissionInfo> mAllPermissions = new ArrayList<MyPermissionInfo>(); MyPermissionGroupInfo(PermissionInfo perm) { name = perm.packageName; packageName = perm.packageName; } MyPermissionGroupInfo(PermissionGroupInfo info) { super (info); } public Drawable loadGroupIcon (Context context, PackageManager pm) { if (icon != 0 ) { return loadUnbadgedIcon(pm); } else { return context.getDrawable(R.drawable.ic_perm_device_info); } } }
2.1.1.3 new MyPermissionInfo 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private static class MyPermissionInfo extends PermissionInfo { CharSequence mLabel; int mNewReqFlags; int mExistingReqFlags; boolean mNew; MyPermissionInfo(PermissionInfo info) { super (info); } }
2.1.2 AppSecurityPermissions.getAllUsedPermissions 获得共享 uid 的权限!
1 2 3 4 5 6 7 8 9 10 11 private void getAllUsedPermissions (int sharedUid, Set<MyPermissionInfo> permSet) { String sharedPkgList[] = mPm.getPackagesForUid(sharedUid); if (sharedPkgList == null || (sharedPkgList.length == 0 )) { return ; } for (String sharedPkg : sharedPkgList) { getPermissionsForPackage(sharedPkg, permSet); } }
2.1.2.1 AppSecurityPermissions.getPermissionsForPackage 继续处理:
1 2 3 4 5 6 7 8 9 10 private void getPermissionsForPackage (String packageName, Set<MyPermissionInfo> permSet) { try { PackageInfo pkgInfo = mPm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS); extractPerms(pkgInfo, permSet, pkgInfo); } catch (NameNotFoundException e) { Log.w(TAG, "Couldn't retrieve permissions for package: " + packageName); } }
不多说了!
2.1.3 AppSecurityPermissions.setPermissions 设置权限,将权限设置到对应的权限组中!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 private void setPermissions (List<MyPermissionInfo> permList) { if (permList != null ) { for (MyPermissionInfo pInfo : permList) { if (localLOGV) Log.i(TAG, "Processing permission:" +pInfo.name); if (!isDisplayablePermission(pInfo, pInfo.mNewReqFlags, pInfo.mExistingReqFlags)) { if (localLOGV) Log.i(TAG, "Permission:" +pInfo.name+" is not displayable" ); continue ; } MyPermissionGroupInfo group = mPermGroups.get(pInfo.group); if (group != null ) { pInfo.mLabel = pInfo.loadLabel(mPm); addPermToList(group.mAllPermissions, pInfo); if (pInfo.mNew) { addPermToList(group.mNewPermissions, pInfo); } } } } for (MyPermissionGroupInfo pgrp : mPermGroups.values()) { if (pgrp.labelRes != 0 || pgrp.nonLocalizedLabel != null ) { pgrp.mLabel = pgrp.loadLabel(mPm); } else { ApplicationInfo app; try { app = mPm.getApplicationInfo(pgrp.packageName, 0 ); pgrp.mLabel = app.loadLabel(mPm); } catch (NameNotFoundException e) { pgrp.mLabel = pgrp.loadLabel(mPm); } } mPermGroupsList.add(pgrp); } Collections.sort(mPermGroupsList, mPermGroupComparator); }
2.1.3.1 AppSecurityPermissions.setPermissions 1 2 3 4 5 6 7 8 9 10 11 12 13 14 private void addPermToList (List<MyPermissionInfo> permList, MyPermissionInfo pInfo) { if (pInfo.mLabel == null ) { pInfo.mLabel = pInfo.loadLabel(mPm); } int idx = Collections.binarySearch(permList, pInfo, mPermComparator); if (localLOGV) Log.i(TAG, "idx=" +idx+", list.size=" +permList.size()); if (idx < 0 ) { idx = -idx-1 ; permList.add(idx, pInfo); } }
2.2 AppSecurityPermissions.getPermissionCount 获得权限的数量:
1 2 3 4 5 6 7 8 public int getPermissionCount (int which) { int N = 0 ; for (int i=0 ; i<mPermGroupsList.size(); i++) { N += getPermissionList(mPermGroupsList.get(i), which).size(); } return N; }
不多说了!
2.2.1 AppSecurityPermissions.getPermissionList 根据 which 指定的标志,返回 grp.mNewPermissions 或者 grp.mAllPermissions1 2 3 4 5 6 7 private List<MyPermissionInfo> getPermissionList (MyPermissionGroupInfo grp, int which) { if (which == WHICH_NEW) { return grp.mNewPermissions; } else { return grp.mAllPermissions; } }
具体的意思很简单了,就不多说了!
3 onClick -> trigger to install 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public void onClick (View v) { if (v == mOk) { if (mOkCanInstall || mScrollView == null ) { if (mSessionId != -1 ) { mInstaller.setPermissionsResult(mSessionId, true ); clearCachedApkIfNeededAndFinish(); } else { startInstall(); } } else { mScrollView.pageScroll(View.FOCUS_DOWN); } } else if (v == mCancel) { setResult(RESULT_CANCELED); if (mSessionId != -1 ) { mInstaller.setPermissionsResult(mSessionId, false ); } clearCachedApkIfNeededAndFinish(); } }
3.1 PackageInstallerService.setPermissionsResult 1 2 3 4 5 6 7 8 9 10 11 12 @Override public void setPermissionsResult (int sessionId, boolean accepted) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG); synchronized (mSessions) { PackageInstallerSession session = mSessions.get(sessionId); if (session != null ) { session.setPermissionsResult(accepted); } } }
3.2 PackageInstallerSession.setPermissionsResult 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void setPermissionsResult (boolean accepted) { if (!mSealed) { throw new SecurityException("Must be sealed to accept permissions" ); } if (accepted) { synchronized (mLock) { mPermissionsAccepted = true ; } mHandler.obtainMessage(MSG_COMMIT).sendToTarget(); } else { destroyInternal(); dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions" , null ); } }
下面,我们继续看 startInstall 方法:
4 PackageInstallerActivity.startInstall 继续分析:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 private void startInstall () { Intent newIntent = new Intent(); newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mPkgInfo.applicationInfo); newIntent.setData(mPackageURI); newIntent.setClass(this , InstallAppProgress.class); String installerPackageName = getIntent().getStringExtra( Intent.EXTRA_INSTALLER_PACKAGE_NAME); if (mOriginatingURI != null ) { newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI); } if (mReferrerURI != null ) { newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI); } if (mOriginatingUid != VerificationParams.NO_UID) { newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid); } if (installerPackageName != null ) { newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, installerPackageName); } if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false )) { newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true ); newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); } if (localLOGV) Log.i(TAG, "downloaded app uri=" +mPackageURI); startActivity(newIntent); finish(); }
关于 start activity,我们有在 ams 的相关内容中看到!
5 InstallAppProgress 5.1 onCreate 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @Override public void onCreate (Bundle icicle) { super .onCreate(icicle); Intent intent = getIntent(); mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO); mPackageURI = intent.getData(); final String scheme = mPackageURI.getScheme(); if (scheme != null && !"file" .equals(scheme) && !"package" .equals(scheme)) { throw new IllegalArgumentException("unexpected scheme " + scheme); } mInstallThread = new HandlerThread("InstallThread" ); mInstallThread.start(); mInstallHandler = new Handler(mInstallThread.getLooper()); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(BROADCAST_ACTION); registerReceiver( mBroadcastReceiver, intentFilter, BROADCAST_SENDER_PERMISSION, null ); initView(); }
InstallAppProgress 显示后,会注册一个接收者,监听下面的广播:1 2 private static final String BROADCAST_ACTION = "com.android.packageinstaller.ACTION_INSTALL_COMMIT" ;
并且发送者要具有这样的权限:1 2 private static final String BROADCAST_SENDER_PERMISSION = "android.permission.INSTALL_PACKAGES" ;
5.1.1 new BroadcastReceiver 用于接收 com.android.packageinstaller.ACTION_INSTALL_COMMIT 广播!!
1 2 3 4 5 6 7 8 9 10 11 12 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive (Context context, Intent intent) { final int statusCode = intent.getIntExtra( PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); if (statusCode == PackageInstaller.STATUS_PENDING_USER_ACTION) { context.startActivity((Intent)intent.getParcelableExtra(Intent.EXTRA_INTENT)); } else { onPackageInstalled(statusCode); } } };
我们在分析 pm install 的时候:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 if (!mPermissionsAccepted) { final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_PERMISSIONS); intent.setPackage(mContext.getPackageManager().getPermissionControllerPackageName()); intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId); try { mRemoteObserver.onUserActionRequired(intent); } catch (RemoteException ignored) { } close(); return ; }
当我们需要用户确认权限的时候,会创建一个 intent,action 为 ACTION_CONFIRM_PERMISSIONS,这里的逻辑,然后会掉
RemoteObserver.onUserActionRequired 方法!
5.2 initView 继续来看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 void initView () { setContentView(R.layout.op_progress); final PackageUtil.AppSnippet as; final PackageManager pm = getPackageManager(); final int installFlags = getInstallFlags(mAppInfo.packageName); if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING )!= 0 ) { Log.w(TAG, "Replacing package:" + mAppInfo.packageName); } if ("package" .equals(mPackageURI.getScheme())) { as = new PackageUtil.AppSnippet(pm.getApplicationLabel(mAppInfo), pm.getApplicationIcon(mAppInfo)); } else { final File sourceFile = new File(mPackageURI.getPath()); as = PackageUtil.getAppSnippet(this , mAppInfo, sourceFile); } mLabel = as.label; PackageUtil.initSnippetForNewApp(this , as, R.id.app_snippet); mStatusTextView = (TextView)findViewById(R.id.center_text); mExplanationTextView = (TextView) findViewById(R.id.explanation); mProgressBar = (ProgressBar) findViewById(R.id.progress_bar); mProgressBar.setIndeterminate(true ); mOkPanel = findViewById(R.id.buttons_panel); mDoneButton = (Button)findViewById(R.id.done_button); mLaunchButton = (Button)findViewById(R.id.launch_button); mOkPanel.setVisibility(View.INVISIBLE); if ("package" .equals(mPackageURI.getScheme())) { try { pm.installExistingPackage(mAppInfo.packageName); onPackageInstalled(PackageInstaller.STATUS_SUCCESS); } catch (PackageManager.NameNotFoundException e) { onPackageInstalled(PackageInstaller.STATUS_FAILURE_INVALID); } } else { final PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); params.referrerUri = getIntent().getParcelableExtra(Intent.EXTRA_REFERRER); params.originatingUri = getIntent().getParcelableExtra(Intent.EXTRA_ORIGINATING_URI); params.originatingUid = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID, UID_UNKNOWN); File file = new File(mPackageURI.getPath()); try { PackageLite pkg = PackageParser.parsePackageLite(file, 0 ); params.setAppPackageName(pkg.packageName); params.setInstallLocation(pkg.installLocation); params.setSize( PackageHelper.calculateInstalledSize(pkg, false , params.abiOverride)); } catch (PackageParser.PackageParserException e) { Log.e(TAG, "Cannot parse package " + file + ". Assuming defaults." ); Log.e(TAG, "Cannot calculate installed size " + file + ". Try only apk size." ); params.setSize(file.length()); } catch (IOException e) { Log.e(TAG, "Cannot calculate installed size " + file + ". Try only apk size." ); params.setSize(file.length()); } mInstallHandler.post(new Runnable() { @Override public void run () { doPackageStage(pm, params); } }); } }
5.2.1 getInstallFlags 1 2 3 4 5 6 7 8 9 10 11 12 13 14 int getInstallFlags (String packageName) { PackageManager pm = getPackageManager(); try { PackageInfo pi = pm.getPackageInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES); if (pi != null ) { return PackageManager.INSTALL_REPLACE_EXISTING; } } catch (NameNotFoundException e) { } return 0 ; }
5.2.2 PackageManagerService.installExistingPackage 安装一个系统中已存在的包,这是一个 hide 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 @Override public int installExistingPackageAsUser (String packageName, int userId) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null ); PackageSetting pkgSetting; final int uid = Binder.getCallingUid(); enforceCrossUserPermission(uid, userId, true , true , "installExistingPackage for user " + userId); if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) { return PackageManager.INSTALL_FAILED_USER_RESTRICTED; } long callingId = Binder.clearCallingIdentity(); try { boolean installed = false ; synchronized (mPackages) { pkgSetting = mSettings.mPackages.get(packageName); if (pkgSetting == null ) { return PackageManager.INSTALL_FAILED_INVALID_URI; } if (!pkgSetting.getInstalled(userId)) { pkgSetting.setInstalled(true , userId); pkgSetting.setHidden(false , userId); mSettings.writePackageRestrictionsLPr(userId); installed = true ; } } if (installed) { if (pkgSetting.pkg != null ) { synchronized (mInstallLock) { prepareAppDataAfterInstallLIF(pkgSetting.pkg); } } sendPackageAddedForUser(packageName, pkgSetting, userId); } } finally { Binder.restoreCallingIdentity(callingId); } return PackageManager.INSTALL_SUCCEEDED; }
不多说了!
5.2.3 onPackageInstalled 发送 INSTALL_COMPLETE 消息给主线程的 Handler:
1 2 3 4 5 6 void onPackageInstalled (int statusCode) { Message msg = mHandler.obtainMessage(INSTALL_COMPLETE); msg.arg1 = statusCode; mHandler.sendMessage(msg); }
5.2.3.1 Handler.handleMessage[INSTALL_COMPLETE] 主要根据安装的结果!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 private Handler mHandler = new Handler() { public void handleMessage (Message msg) { switch (msg.what) { case INSTALL_COMPLETE: if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false )) { Intent result = new Intent(); result.putExtra(Intent.EXTRA_INSTALL_RESULT, msg.arg1); setResult(msg.arg1 == PackageInstaller.STATUS_SUCCESS ? Activity.RESULT_OK : Activity.RESULT_FIRST_USER, result); clearCachedApkIfNeededAndFinish(); return ; } mProgressBar.setVisibility(View.GONE); int centerTextLabel; int centerExplanationLabel = -1 ; if (msg.arg1 == PackageInstaller.STATUS_SUCCESS) { mLaunchButton.setVisibility(View.VISIBLE); ((ImageView)findViewById(R.id.center_icon)) .setImageDrawable(getDrawable(R.drawable.ic_done_92)); centerTextLabel = R.string.install_done; mLaunchIntent = getPackageManager().getLaunchIntentForPackage( mAppInfo.packageName); boolean enabled = false ; if (mLaunchIntent != null ) { List<ResolveInfo> list = getPackageManager(). queryIntentActivities(mLaunchIntent, 0 ); if (list != null && list.size() > 0 ) { enabled = true ; } } if (enabled) { mLaunchButton.setOnClickListener(InstallAppProgress.this ); } else { mLaunchButton.setEnabled(false ); } } else if (msg.arg1 == PackageInstaller.STATUS_FAILURE_STORAGE){ showDialogInner(DLG_OUT_OF_SPACE); return ; } else { ((ImageView)findViewById(R.id.center_icon)) .setImageDrawable(getDrawable(R.drawable.ic_report_problem_92)); centerExplanationLabel = getExplanationFromErrorCode(msg.arg1); centerTextLabel = R.string.install_failed; mLaunchButton.setVisibility(View.GONE); } if (centerExplanationLabel != -1 ) { mExplanationTextView.setText(centerExplanationLabel); findViewById(R.id.center_view).setVisibility(View.GONE); ((TextView)findViewById(R.id.explanation_status)).setText(centerTextLabel); findViewById(R.id.explanation_view).setVisibility(View.VISIBLE); } else { ((TextView)findViewById(R.id.center_text)).setText(centerTextLabel); findViewById(R.id.center_view).setVisibility(View.VISIBLE); findViewById(R.id.explanation_view).setVisibility(View.GONE); } mDoneButton.setOnClickListener(InstallAppProgress.this ); mOkPanel.setVisibility(View.VISIBLE); break ; default : break ; } } };
不多说了!
5.3 doPackageStage 到这里,已经和 pm install 中很类似了:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 private void doPackageStage (PackageManager pm, PackageInstaller.SessionParams params) { final PackageInstaller packageInstaller = pm.getPackageInstaller(); PackageInstaller.Session session = null ; try { final String packageLocation = mPackageURI.getPath(); final File file = new File(packageLocation); final int sessionId = packageInstaller.createSession(params); final byte [] buffer = new byte [65536 ]; session = packageInstaller.openSession(sessionId); final InputStream in = new FileInputStream(file); final long sizeBytes = file.length(); final OutputStream out = session.openWrite("PackageInstaller" , 0 , sizeBytes); try { int c; while ((c = in.read(buffer)) != -1 ) { out.write(buffer, 0 , c); if (sizeBytes > 0 ) { final float fraction = ((float ) c / (float ) sizeBytes); session.addProgress(fraction); } } session.fsync(out); } finally { IoUtils.closeQuietly(in); IoUtils.closeQuietly(out); } Intent broadcastIntent = new Intent(BROADCAST_ACTION); PendingIntent pendingIntent = PendingIntent.getBroadcast( InstallAppProgress.this , sessionId, broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT); session.commit(pendingIntent.getIntentSender()); } catch (IOException e) { onPackageInstalled(PackageInstaller.STATUS_FAILURE); } finally { IoUtils.closeQuietly(session); } }
按照结束后,pendingIntent 会被触发,然后发送 com.android.packageinstaller.ACTION_INSTALL_COMMIT 广播!
6 onCreateDialog -> notify user 下面我们来看下 onCreateDialog 的函数逻辑,这个会在安装过程中提示用户:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 @Override public Dialog onCreateDialog (int id, Bundle bundle) { switch (id) { case DLG_UNKNOWN_SOURCES: return new AlertDialog.Builder(this ) .setMessage(R.string.unknown_apps_dlg_text) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { public void onClick (DialogInterface dialog, int which) { Log.i(TAG, "Finishing off activity so that user can navigate to settings manually" ); finishAffinity(); }}) .setPositiveButton(R.string.settings, new DialogInterface.OnClickListener() { public void onClick (DialogInterface dialog, int which) { Log.i(TAG, "Launching settings" ); launchSecuritySettings(); } }) .setOnCancelListener(this ) .create(); case DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES: return new AlertDialog.Builder(this ) .setMessage(R.string.unknown_apps_admin_dlg_text) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick (DialogInterface dialog, int which) { finish(); } }) .setOnCancelListener(this ) .create(); case DLG_PACKAGE_ERROR : return new AlertDialog.Builder(this ) .setMessage(R.string.Parse_error_dlg_text) .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { public void onClick (DialogInterface dialog, int which) { finish(); } }) .setOnCancelListener(this ) .create(); case DLG_OUT_OF_SPACE: CharSequence appTitle = mPm.getApplicationLabel(mPkgInfo.applicationInfo); String dlgText = getString(R.string.out_of_space_dlg_text, appTitle.toString()); return new AlertDialog.Builder(this ) .setMessage(dlgText) .setPositiveButton(R.string.manage_applications, new DialogInterface.OnClickListener() { public void onClick (DialogInterface dialog, int which) { Intent intent = new Intent("android.intent.action.MANAGE_PACKAGE_STORAGE" ); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); finish(); } }) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { public void onClick (DialogInterface dialog, int which) { Log.i(TAG, "Canceling installation" ); finish(); } }) .setOnCancelListener(this ) .create(); case DLG_INSTALL_ERROR : CharSequence appTitle1 = mPm.getApplicationLabel(mPkgInfo.applicationInfo); String dlgText1 = getString(R.string.install_failed_msg, appTitle1.toString()); return new AlertDialog.Builder(this ) .setNeutralButton(R.string.ok, new DialogInterface.OnClickListener() { public void onClick (DialogInterface dialog, int which) { finish(); } }) .setMessage(dlgText1) .setOnCancelListener(this ) .create(); case DLG_NOT_SUPPORTED_ON_WEAR: return new AlertDialog.Builder(this ) .setMessage(R.string.wear_not_allowed_dlg_text) .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { public void onClick (DialogInterface dialog, int which) { setResult(RESULT_OK); clearCachedApkIfNeededAndFinish(); } }) .setOnCancelListener(this ) .create(); } return null ; }
不多说了!
6.1 launchSecuritySettings 进入设置,让用户选择是否打开未知来源设置:1 2 3 4 private void launchSecuritySettings () { Intent launchSettingsIntent = new Intent(Settings.ACTION_SECURITY_SETTINGS); startActivityForResult(launchSettingsIntent, REQUEST_ENABLE_UNKNOWN_SOURCES); }