主要是PackageInstallerActivity类, 安装过程中一些状态转换与判断都在这个Activity中进行.

入口函数onCreated方法。

    @Overrideprotected void onCreate(Bundle icicle) {super.onCreate(icicle);if (icicle != null) {mAllowUnknownSources = icicle.getBoolean(ALLOW_UNKNOWN_SOURCES_KEY);}mPm = getPackageManager();//PackageManagermIpm = AppGlobals.getPackageManager();//IPackageManagermAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);mInstaller = mPm.getPackageInstaller();//PackageInstallermUserManager = (UserManager) getSystemService(Context.USER_SERVICE);final Intent intent = getIntent();mCallingPackage = intent.getStringExtra(EXTRA_CALLING_PACKAGE);mSourceInfo = intent.getParcelableExtra(EXTRA_ORIGINAL_SOURCE_INFO);//ApplicationInfomOriginatingUid = intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID,PackageInstaller.SessionParams.UID_UNKNOWN);mOriginatingPackage = (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN)? getPackageNameForUid(mOriginatingUid) : null;//The package name corresponding to mOriginatingUidfinal Uri packageUri;if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);//sessionIdfinal 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 there's nothing to do, quietly slip into the etherif (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;}boolean wasSetUp = processPackageUri(packageUri);if (!wasSetUp) {return;}// load dummy layout with OK button disabled until we override this layout in// startInstallConfirmbindUi(R.layout.install_confirm, false);checkIfAllowedAndInitiateInstall();}

(1)这里看下processPackageUri()

    /*** Parse the Uri and set up the installer for this package.* 解析URI,并建立PackageInstaller* @param packageUri The URI to parse** @return {@code true} iff the installer could be set up*/private boolean processPackageUri(final Uri packageUri) {mPackageURI = packageUri;final String scheme = packageUri.getScheme();switch (scheme) {case SCHEME_PACKAGE: {try {mPkgInfo = mPm.getPackageInfo(packageUri.getSchemeSpecificPart(),PackageManager.GET_PERMISSIONS| PackageManager.MATCH_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;}mAppSnippet = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo),mPm.getApplicationIcon(mPkgInfo.applicationInfo));} break;case ContentResolver.SCHEME_FILE: {File sourceFile = new File(packageUri.getPath());//根据uri创建一个文件对象PackageParser.Package parsed = PackageUtil.getPackageInfo(this, sourceFile);//解析文件// Check for parse errorsif (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());//解析获得PackageInfo,获取权限部分mAppSnippet = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);//获取PackageUtil.AppSnippet} break;default: {throw new IllegalArgumentException("Unexpected URI scheme " + packageUri);}}return true;}

AppSnippet中只有两个属性,应用的lable,icon。

可以看出 processPackageUrl调用framework的api获取了apk的权限信息与应用名称与图标。

(2)然后就是判断该包是否允许安装,如果允许则初始化准备安装。首先在checkIfAllowedAndInitiateInstall中判断是否是未知来源的应用, 一般是开启允许未知来源的。直接开启初始化安装。

    /*** Check if it is allowed to install the package and initiate install if allowed. If not allowed* show the appropriate dialog.*/private void checkIfAllowedAndInitiateInstall() {// Check for install apps user restriction first.首先检查用户限制final int installAppsRestrictionSource = mUserManager.getUserRestrictionSource(UserManager.DISALLOW_INSTALL_APPS, Process.myUserHandle());if ((installAppsRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {showDialogInner(DLG_INSTALL_APPS_RESTRICTED_FOR_USER);return;} else if (installAppsRestrictionSource != UserManager.RESTRICTION_NOT_SET) {startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));//激活设备权限finish();return;}if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {initiateInstall();//初始化安装} else {// Check for unknown sources restriction检查未知源限制final int unknownSourcesRestrictionSource = mUserManager.getUserRestrictionSource(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, Process.myUserHandle());if ((unknownSourcesRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER);} else if (unknownSourcesRestrictionSource != UserManager.RESTRICTION_NOT_SET) {startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));finish();} else {handleUnknownSources();}}}

(3)initiateInstall仅仅获取一下本机是否安装了该应用, 如果安装获取到该应用的信息.

    private void initiateInstall() {String pkgName = mPkgInfo.packageName;// Check if there is already a package on the device with this name// but it has been renamed to something else.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;}// Check if package is already installed. display confirmation dialog if replacing pkgtry {// This is a little convoluted because we want to get all uninstalled// apps, but this may include apps with just data, and if it is just// data we still want to count it as "installed".如果仅有一个应用的数据,仍然认为是已安装的。mAppInfo = mPm.getApplicationInfo(pkgName,PackageManager.MATCH_UNINSTALLED_PACKAGES);if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {mAppInfo = null;//如果没有安装,将ApplicationInfo置空}} catch (NameNotFoundException e) {mAppInfo = null;}startInstallConfirm();}

(4)最后startInstallConfirm()方法中显示了界面中显示的权限列表以及确认取消列表。

    private void startInstallConfirm() {// We might need to show permissions, load layout with permissionsif (mAppInfo != null) {bindUi(R.layout.install_confirm_perm_update, true);} else {bindUi(R.layout.install_confirm_perm, true);}((TextView) findViewById(R.id.install_confirm_question)).setText(R.string.install_confirm_question);ViewPager viewPager = (ViewPager)findViewById(R.id.pager);TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);TabsTitleAdapter adapter = new TabsTitleAdapter(this);viewPager.setAdapter(adapter);tabLayout.setTabMode(TabLayout.MODE_FIXED);tabLayout.setupWithViewPager(viewPager);// If the app supports runtime permissions the new permissions will如果应用程序支持运行时权限,// be requested at runtime, hence we do not show them at install.则将在运行时请求新权限,因此我们不会在安装时显示它们。boolean supportsRuntimePermissions = mPkgInfo.applicationInfo.targetSdkVersion>= Build.VERSION_CODES.M;boolean permVisible = false;mScrollView = null;mOkCanInstall = false;int msg = 0;int position = 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);}tabLayout.addTab(tabLayout.newTab().setTag(TAB_ID_NEW).setText(getText(R.string.newPerms)));adapter.addTab(position, getText(R.string.newPerms).toString(), mScrollView);position ++;} else  {tabLayout.setVisibility(View.GONE);}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));tabLayout.addTab(tabLayout.newTab().setTag(TAB_ID_ALL).setText(getText(R.string.allPerms)));adapter.addTab(position, getText(R.string.allPerms).toString(), root);}if (!permVisible) {if (mAppInfo != null) {// This is an update to an application, but there are no// permissions at all.msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0? R.string.install_confirm_question_update_system_no_perms: R.string.install_confirm_question_update_no_perms;} else {// This is a new application with no permissions.msg = R.string.install_confirm_question_no_perms;}// We do not need to show any permissions, load layout without permissionsbindUi(R.layout.install_confirm, true);mScrollView = null;}if (msg != 0) {((TextView)findViewById(R.id.install_confirm_question)).setText(msg);}if (mScrollView == null) {// There is nothing to scroll view, so the ok button is immediately// set to install.mOk.setText(R.string.install);mOkCanInstall = true;} else {mScrollView.setFullScrollAction(new Runnable() {@Overridepublic void run() {mOk.setText(R.string.install);mOkCanInstall = true;}});}}

查看点击OK的事件,确定安装事件为:

    private void startInstall() {// Start subactivity to actually install the application启动子Activity,进行实际安装Intent newIntent = new Intent();newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,mPkgInfo.applicationInfo);newIntent.setData(mPackageURI);newIntent.setClass(this, InstallInstalling.class);//跳转到InstallInstalling类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 != PackageInstaller.SessionParams.UID_UNKNOWN) {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();}

好吧,安装确认后跳转到安装进度页面,具体安装过程也在这个界面中。

(5)在InstallInstalling的onCreate方法里,有两个主要步骤,一个是PackageParser.parsePackageLite,他解析apk里面的部分信息出来;另一个是getPackageInstaller().createSession,它负责创建一个会话,用于后续的安装,会话是通过binder调用在PackageInstallerServices里面创建的。

    @Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setAcuteagContentView(R.layout.install_installing);ApplicationInfo appInfo = getIntent().getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);//获取ApplicationInfomPackageURI = getIntent().getData();//apk对应的uriif ("package".equals(mPackageURI.getScheme())) {try {getPackageManager().installExistingPackage(appInfo.packageName);launchSuccess();} catch (PackageManager.NameNotFoundException e) {launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);}} else {final File sourceFile = new File(mPackageURI.getPath());PackageUtil.initSnippetForNewApp(this, PackageUtil.getAppSnippet(this, appInfo,sourceFile), R.id.app_snippet);if (savedInstanceState != null) {mSessionId = savedInstanceState.getInt(SESSION_ID);mInstallId = savedInstanceState.getInt(INSTALL_ID);// Reregister for result; might instantly call back if result was delivered while// activity was destroyedtry {InstallEventReceiver.addObserver(this, mInstallId,this::launchFinishBasedOnResult);//添加监听器} catch (EventResultPersister.OutOfIdsException e) {// Does not happen}} else {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 {PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file, 0);//解析APKparams.setAppPackageName(pkg.packageName);params.setInstallLocation(pkg.installLocation);params.setSize(PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride));} catch (PackageParser.PackageParserException e) {Log.e(LOG_TAG, "Cannot parse package " + file + ". Assuming defaults.");Log.e(LOG_TAG,"Cannot calculate installed size " + file + ". Try only apk size.");params.setSize(file.length());} catch (IOException e) {Log.e(LOG_TAG,"Cannot calculate installed size " + file + ". Try only apk size.");params.setSize(file.length());}try {mInstallId = InstallEventReceiver.addObserver(this, EventResultPersister.GENERATE_NEW_ID,this::launchFinishBasedOnResult);} catch (EventResultPersister.OutOfIdsException e) {launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);}try {mSessionId = getPackageManager().getPackageInstaller().createSession(params);//创建一个Session} catch (IOException e) {launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);}}mCancelButton = (Button) findViewById(R.id.cancel_button);mCancelButton.setOnClickListener(view -> {if (mInstallingTask != null) {mInstallingTask.cancel(true);}if (mSessionId > 0) {getPackageManager().getPackageInstaller().abandonSession(mSessionId);mSessionId = 0;}setResult(RESULT_CANCELED);finish();});mSessionCallback = new InstallSessionCallback();}}

(6)在InstallInstalling.java的onResume启动一个异步task InstallingAsyncTask

    @Overrideprotected void onResume() {super.onResume();// This is the first onResume in a single life of the activityif (mInstallingTask == null) {PackageInstaller installer = getPackageManager().getPackageInstaller();PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);if (sessionInfo != null && !sessionInfo.isActive()) {mInstallingTask = new InstallingAsyncTask();//异步任务mInstallingTask.execute();} else {// we will receive a broadcast when the install is finishedmCancelButton.setEnabled(false);setFinishOnTouchOutside(false);}}}

(7)InstallingAsyncTask主要是操作session,session也通过binder方式来操作,主要完成的工作是通过之前建立的session,创建文件夹,将文件拷贝到安装目录下面

    /*** Send the package to the package installer and then register a event result observer that* will call {@link #launchFinishBasedOnResult(int, int, String)}*/private final class InstallingAsyncTask extends AsyncTask<Void, Void,PackageInstaller.Session> {volatile boolean isDone;@Overrideprotected PackageInstaller.Session doInBackground(Void... params) {PackageInstaller.Session session;try {session = getPackageManager().getPackageInstaller().openSession(mSessionId);//openSession} catch (IOException e) {return null;}session.setStagingProgress(0);try {File file = new File(mPackageURI.getPath());try (InputStream in = new FileInputStream(file)) {long sizeBytes = file.length();try (OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes)) {byte[] buffer = new byte[1024 * 1024];while (true) {int numRead = in.read(buffer);if (numRead == -1) {session.fsync(out);break;}if (isCancelled()) {session.close();break;}out.write(buffer, 0, numRead);if (sizeBytes > 0) {float fraction = ((float) numRead / (float) sizeBytes);session.addProgress(fraction);}}}}return session;} catch (IOException | SecurityException e) {Log.e(LOG_TAG, "Could not write package", e);session.close();return null;} finally {synchronized (this) {isDone = true;notifyAll();}}}

目前为止apk的安装都只是在PackageInstaller apk和framework的PackageInstaller框架之间交换,还没有涉及到PMS,这一点是怎么做到的呢,如果没有PMS参与,就没有参考文档里面我们了解的那些对manifest的解析、权限处理、文件拷贝和dex优化等相关流程,这里就产生了割裂感。

(8)PMS在上层的接口类是APM,通过PM类在PackageInstaller和PackageInstallerSession的引用就能找到相关的关联关系。在异步taskInstallingAsyncTask的方法中调用commit。

        @Overrideprotected void onPostExecute(PackageInstaller.Session session) {if (session != null) {Intent broadcastIntent = new Intent(BROADCAST_ACTION);broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);broadcastIntent.setPackage(getPackageManager().getPermissionControllerPackageName());broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);PendingIntent pendingIntent = PendingIntent.getBroadcast(InstallInstalling.this,mInstallId,broadcastIntent,PendingIntent.FLAG_UPDATE_CURRENT);session.commit(pendingIntent.getIntentSender());mCancelButton.setEnabled(false);setFinishOnTouchOutside(false);} else {getPackageManager().getPackageInstaller().abandonSession(mSessionId);if (!isCancelled()) {launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);}}}}

PackageInstaller.Session的commit方法:

        public void commit(@NonNull IntentSender statusReceiver) {try {mSession.commit(statusReceiver, false);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}

然后,通过binder,在PackageInstallerSession.java里,调用commit,给handler发一个消息MSG_COMMIT

         final PackageInstallObserverAdapteradapter = new PackageInstallObserverAdapter(mContext,statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();

然后Handler.Callback mHandlerCallback再调用commitLocked(),这里就开始调用到PMS的安装接口了,然后就是PMS的流程

     private void commitLocked() throwsPackageManagerException {…// Unpack nativelibrariesextractNativeLibraries(mResolvedStageDir, params.abiOverride);。。。mRelinquished =true;mPm.installStage(mPackageName,stageDir, stageCid, localObserver, params,installerPackageName, installerUid, user, mCertificates);}

下面就进入frameworks层的PackageManagerService里去了。

2、AppSecurityPermissions

AppSecurityPermissions中的mPermsList保存了应用的权限信息。

    public AppSecurityPermissions(Context context, PackageInfo info) {this(context);Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>();if(info == null) {return;}mPackageName = info.packageName;// Convert to a PackageInfoPackageInfo installedPkgInfo = null;// Get requested permissionsif (info.requestedPermissions != null) {try {installedPkgInfo = mPm.getPackageInfo(info.packageName,PackageManager.GET_PERMISSIONS);//获得一个PackageInfo} catch (NameNotFoundException e) {}extractPerms(info, permSet, installedPkgInfo);//调用extractPerms方法,permission保存在permSet中}// Get permissions related to shared user if anyif (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);}}// Retrieve list of permissionsmPermsList.addAll(permSet);setPermissions(mPermsList);}

extractPerms方法,从PackageInfo对象中获取的权限保存到permSet中。

    private void extractPerms(PackageInfo info, Set<MyPermissionInfo> permSet,PackageInfo installedPkgInfo) {String[] strList = info.requestedPermissions;//列出来AndroidManifest中所需要的权限,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);//获取PermissionInfoif (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)) {// This is not a permission that is interesting for the user// to see, so skip it.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 {// We could be here either because the permission// didn't originally specify a group or the group it// gave couldn't be found.  In either case, we consider// its group to be the permission's package name.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;// This is a new permission if the app is already installed and// doesn't currently hold this permission.myPerm.mNew = newPerm;permSet.add(myPerm);//权限添加到permSet中} catch (NameNotFoundException e) {Log.i(TAG, "Ignoring unknown permission:"+permName);}}}

第三个参数意思是查看下这个包以前是否安装过,对于安装过的程序,在展示权限信息的时候,会只展示这个新包要求的新的权限,相对于的原有的上一个版本已经出现过的就不再做展示啦。

Android8.1--PackageInstaller相关推荐

  1. Android8.1修改packageinstaller安装指定应用不弹窗静默安装

    Android8.1系统上,客户app会调用通用的app更新安装方式进行安装更新,要求安装其应用时不能弹出安装界面,需要后台静默安装.这个时候需要修改packageinstaller来实现. 1.ap ...

  2. Android 系统(203)---Android包管理机制(一)PackageInstaller的初始化

    Android包管理机制(一)PackageInstaller的初始化 转自:https://blog.csdn.net/itachi85/article/details/81024903 前言 包管 ...

  3. com.android.packageinstaller,Android包管理机制(二)PackageInstaller安装APK

    前言 在本系列上一篇文章Android包管理机制(一)PackageInstaller的初始化中我们学习了PackageInstaller是如何初始化的,这一篇文章我们接着学习PackageInsta ...

  4. android8显示广播,关于android8.0静态接收广播

    android8.0很多广播不能静态注册接受 查看log显示 Background execution not allowed: receiving 查看代码frameworks/base/servi ...

  5. Android8.0运行时权限策略变化和适配方案

    版权声明:转载必须注明本文转自严振杰的博客:http://blog.yanzhenjie.com Android8.0也就是Android O即将要发布了,有很多新特性,目前我们可以通过Android ...

  6. android8.1内核编译,Android8.1.0安卓源码编译

    换中科大源18.04 由于新安装的Ubuntu默认配置为Ubuntu官方源,基本软件包下载速度较慢,下面修改软件源: 备份下sources.list sudo cp /etc/apt/sources. ...

  7. uiautomatorviewer_【问题】解决Android8.0以上运行不了uiautomatorviewer的办法

    问题:Android8.0以上,打开uiautomatorviewer.bat,报错,提示:Unexpected error while obtaining UI hierarchy 解决办法: 可以 ...

  8. Android8.0恢复出厂设置失败-BroadcastQueue: Background execution not allowed

    Android7.0恢复出厂设置调用下面的代码可正常恢复出厂 Intent intent = new Intent(Intent. ACTION_MASTER_CLEAR);intent.addFla ...

  9. 华为手机 android8.0APP更新时出现安装包解析异常的提示及安装闪退(无反应)问题

    华为手机 android8.0APP更新时出现安装包解析异常的提示及安装闪退(无反应)问题 参考文章: (1)华为手机 android8.0APP更新时出现安装包解析异常的提示及安装闪退(无反应)问题 ...

  10. Android8.0适配那点事(二)

    小伙伴们,咱们今天咱继续对Android8.0的适配进行分解,今天将针对启动页,版本适配和系统限制等进行"啃食" 1.启动页适配 近日,我无意中发现应用在8.0系统上面启动页崩溃, ...

最新文章

  1. python使用sklearn的ConfusionMatrixDisplay来可视化混淆矩阵
  2. IT一大道至简: 文章列表
  3. HDU - 1728 逃离迷宫(bfs)
  4. 安卓前端布局Android,Android开发的几种常见布局
  5. 生产问题分析!delete in子查询不走索引?!
  6. MacOS使用ffmpeg报错killed的问题与解决
  7. Linux操作Oracle(3)——Oracle OPatch打补丁遇到问题详细汇总详细记录
  8. 提升研发效能没那么难,看优酷的最佳实践!
  9. 计算机网络课程设计小型企业局域网的组建,计算机网络课程设计小型企业局域网的组建.doc...
  10. 博科导向器升级固件-U盘
  11. 展锐物联网芯片8910DM获德国电信认证
  12. 倪光南——世人笑我太疯癫,我笑他人看不穿
  13. InoReader—— 轻便快捷的在线 RSS 阅读器
  14. python解压多层压缩包,兼容tar.gz .tgz .zip .7z .gz
  15. 计算机网络离不开光缆,无线网络论文.doc
  16. 博途plc连接电脑_西门子博途怎么上载plc程序
  17. navicat导入excel数据日期是1900年的问题
  18. 把好莱坞大片搬上区块链,好莱坞链(HLW Chain)要用区块链变革电影产业
  19. html怎么让图片自动动起来,使用css让图片动起来
  20. c语言 简化乘法,简单的C语言移位计算整形乘法和除法值

热门文章

  1. 红外遥控器发射端原理与实现
  2. 测试—数据库性能测试
  3. 摩客怎么设置安卓的dp_Android中的dp
  4. ES系列3-ES中基本概念
  5. 模仿新浪手机频道焦点广告图片切换的代码
  6. 假设某系统的登录账号是“Admin“,密码是“p888888“;编程实现要求用户输入账号和密码,当验证通过时显示“登录成功”,当验证失败时显示“账号或密码错误”
  7. android 自定义布局 attribute·,android 自定义控件之xml---- attributeset attrs
  8. 如何查询当前本机使用的代理服务器IP地址
  9. python数据抓取之pyquery包
  10. EXOduino开源手外骨骼有几个自由度?