Android App installation pop-up window display process

1. APP INSTALLATION

1. Common installation methods
System application and prefabricated application installation - complete when booting, there is no installation interface, and the installation is completed in the constructor of PKMS

Internet download or third-party application installation - call PackageManager.installPackages(), there is an installation interface.

ADB tool installation - there is no installation interface, it starts the pm script, then calls the com.android.commands.pm.Pm class, and then calls PMS.installStage() to complete the installation.
insert image description here2. APK signature verification understanding

V1签名apk-signature-v1-location.png只是校验了apk资源,并没有约束zip,签名信息存储在zip/META-INF中。

v2签名是一个对全文件进行签名的方案,能提供更快的应用安装时间、对未授权APK文件的更改提供更多保护.

3. APK installation process

开机后扫描应用安装目录和系统App目录,解析其中的apk文件将相关信息加载到PKMS中的数据结构中,同时对于没有对应数据目录的App生成对应的数据目录
注册包名App等信息、以及相关的四大组件到PMS中
将解析到的数据同步到/data/system/packages.xml中

4. Comprehension of directories involved in App installation
System App installation directory

1. /system/app: Android system App path
2. /system/priv-app: Same as above, but with higher priority than /system/app permission, you can get ApplicationInfo.PRIVATE_FLAG_PRIVILEGED special permission
3. /vendor/app: odm Or OEM prefabricated system App directory
4, /vendor/priva-app: Same as above

Ordinary application App installation directory

/data/app: The directory where the user App program is installed. Apk will be copied to this directory during installation

user data directory

/data/data: stores application data, whether it is a system app or a normal app, the user data generated by the app is stored in the /data/data/package name/directory.

App registry directory

/data/system
1. packages.xml:
record the permissions, flags, ts, version, uesrid and other information of the apk. These information are mainly obtained by parsing the AndroidManifest.xml of the apk. When the system performs operations such as program installation, uninstallation and update, will update the file.
2. packages-backup.xml: backup file
3. packages-stopped.xml: record the Package information of the application that was forcibly stopped by the user
4. packages-stopped-backup.xml: backup of the pakcages-stopped.xml file
5. packages. list : Record the data information of APKs that are not included with the system. When these APKs change, the file will be updated

5. Analysis of package.xml file

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<packages>
    <version sdkVersion="xxx" databaseVersion="xxx" fingerprint="xxx" />
    <version volumeUuid="xxx" sdkVersion="xxx" databaseVersion="xxx" fingerprint="xxx" />

    <permissions>
        <item name="android.permission.REAL_GET_TASKS" package="android" protection="18" />
        ...
    </permissions>   
  
    <package name="com.android.providers.telephony" codePath="/system/priv-app/TelephonyProvider" nativeLibraryPath="/system/priv-app/TelephonyProvider/lib" publicFlags="1007402501" privateFlags="8" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="25" versionName="7.1.2" applicationName="电话和短信存储" sharedUserId="1001" isOrphaned="true">
        <sigs count="1">
            <cert index="1" key="xxx" />
        </sigs>
        <perms>
            <item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
            ...
        </perms>
        <proper-signing-keyset identifier="1" />
    </package>  
    ...       
    
    <updated-package name="xxx.xxx.xxx" codePath="/system/app/xxx" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="11" nativeLibraryPath="/system/app/xxx/lib" primaryCpuAbi="armeabi-v7a" sharedUserId="1000" />


    <shared-user name="android.media" userId="10005">
        <sigs count="1">
            <cert index="2" />
        </sigs>
        <perms>
            <item name="android.permission.ACCESS_CACHE_FILESYSTEM" granted="true" flags="0" />
            ...
        </perms>
    </shared-user>
    ...
</packages>   


insert image description hereThe class diagram relationship BasePermission corresponding to package.xml

BasePermission corresponds to the subtag item of the permissions tag in packages.xml, and a BasePermission will be generated for each permission defined above.
protection: There are four levels:
1. Normal authority (normal)
2. Runtime authority (dangerous)
3. Signature authority (signature)
4. Special authority (privileged)

    <permissions>
        <item name="android.permission.REAL_GET_TASKS" package="android" protection="18" />
        ...
    <permissions/>  

PermissionsState
insert image description here

PermissionState corresponds to the content in the subtag tag in the tag

<package name="com.android.providers.telephony" codePath="/system/priv-app/TelephonyProvider" nativeLibraryPath="/system/priv-app/TelephonyProvider/lib" publicFlags="1007402501" privateFlags="8" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="25" versionName="7.1.2" applicationName="电话和短信存储" sharedUserId="1001" isOrphaned="true">
        <perms>
            <item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
            ...
        </perms>
</package>


PackageSignatures

PackageSignatures对应的是<package>标签中的子标签<sigs>标签中的内容
<package name="com.android.providers.telephony" codePath="/system/priv-app/TelephonyProvider" nativeLibraryPath="/system/priv-app/TelephonyProvider/lib" publicFlags="1007402501" privateFlags="8" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="25" versionName="7.1.2" applicationName="电话和短信存储" sharedUserId="1001" isOrphaned="true">
        <sigs count="1">
            <cert index="1" key="xxx" />
        </sigs>
</package>


PackageSetting

The data structure class PackageSetting is the class corresponding to the information label of the installation package recorded in packages.xml. You can see that PackageSetting inherits the PackageSettingBase class, and the PackageSettingBase class inherits from the SettingBase class. The basic information of the application is stored in the member variables of the PackageSettingBase class, the signature is stored in PackageSignatures, and the permission status is stored in the PermissionsState of the parent class's SettingBase.

 <package name="com.android.providers.telephony" codePath="/system/priv-app/TelephonyProvider" nativeLibraryPath="/system/priv-app/TelephonyProvider/lib" publicFlags="1007402501" privateFlags="8" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="25" versionName="7.1.2" applicationName="电话和短信存储" sharedUserId="1001" isOrphaned="true">
        <sigs count="1">
            <cert index="1" key="xxx" />
        </sigs>
        <perms>
            <item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
            ...
        </perms>
        <proper-signing-keyset identifier="1" />
</package>  

SharedUserSetting

The data structure class SharedUserSetting is the class corresponding to the information label of the installation package recorded in packages.xml. It has a common parent class, SettingBase, with PackageSetting, that is, the permission information is saved through the PermissionsState of the parent class. SharedUserSetting is designed to describe application information with the same sharedUserId. Its member variable packages saves all application information references with the same sharedUserId, and the member variable userId records the UID shared by multiple APKs. The signatures of the applications that share users are the same, and the signatures are stored in the member variable signatures (one thing to note here, because the signatures are the same, it is easy for the Android runtime to retrieve other applications that have the same sharedUserId for an application).

insert image description here

 <shared-user name="android.media" userId="10005">
        <sigs count="1">
            <cert index="2" />
        </sigs>
        <perms>
            <item name="android.permission.ACCESS_CACHE_FILESYSTEM" granted="true" flags="0" />
            ...
        </perms>
</shared-user>

Settings : package.xml ultimate housekeeper class
insert image description here

2. The overall process of APP installation

Code repository: http://androidxref.com/9.0.0_r3/xref/packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallStart.java

1. Install APP code entry

    <activity android:name=".InstallStart"
    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="file"/>
        <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="file"/>
        <data android:scheme="package"/>
        <data android:scheme="content"/>
    </intent-filter>
    <intent-filter android:priority="1">
        <action android:name="android.content.pm.action.CONFIRM_PERMISSIONS"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
    </activity>

2. According to the different Scheme protocols of Uri, jump to different interfaces.
The content protocol jumps to InstallStaging, and the package protocol jumps to PackageInstallerActivity.

 protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ......
        Intent nextActivity = new Intent(intent);
        nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
        // 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_ORIGINAL_SOURCE_INFO, sourceInfo);
        nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);

        //1、content的Uri协议 : InstallStaging 
        //2、package的Url协议:PackageInstallerActivity 
        if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
    
    
            nextActivity.setClass(this, PackageInstallerActivity.class);
        } else {
    
    
            Uri packageUri = intent.getData();

            if (packageUri != null && (packageUri.getScheme().equals(ContentResolver.SCHEME_FILE)
                    || packageUri.getScheme().equals(ContentResolver.SCHEME_CONTENT))) {
    
    
                // Copy file to prevent it from being changed underneath this process
                   //1、content的Uri协议 : InstallStaging 
                nextActivity.setClass(this, InstallStaging.class);
            } else if (packageUri != null && packageUri.getScheme().equals(
                    PackageInstallerActivity.SCHEME_PACKAGE)) {
    
    
                //package的Url协议:PackageInstallerActivity 
                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();
    }

3. Introduction to InstallStaging class

Main content: Convert the Uri of the content protocol to the Uri of the package protocol, and then write it into the mStagedFile file through IO.
Function: It mainly plays the role of conversion, converts the Uri of the content protocol to the package protocol, and then jumps to PackageInstallerActivity

@Override
  protected void onResume() {
    
    
      super.onResume();
      if (mStagingTask == null) {
    
    
          if (mStagedFile == null) {
    
    
              try {
    
    
                  mStagedFile = TemporaryFileManager.getStagedFile(this);
              } catch (IOException e) {
    
    
                  showError();
                  return;
              }
          }
          mStagingTask = new StagingAsyncTask();
          mStagingTask.execute(getIntent().getData());
      }
  }


 private final class StagingAsyncTask extends AsyncTask<Uri, Void, Boolean> {
    
    
        @Override
        protected Boolean doInBackground(Uri... params) {
    
    
            if (params == null || params.length <= 0) {
    
    
                return false;
            }
            Uri packageUri = params[0];
            try (InputStream in = getContentResolver().openInputStream(packageUri)) {
    
    
                if (in == null) {
    
    
                    return false;
                }
                try (OutputStream out = new FileOutputStream(mStagedFile)) {
    
    
                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    while ((bytesRead = in.read(buffer)) >= 0) {
    
    
                        if (isCancelled()) {
    
    
                            return false;
                        }
                        out.write(buffer, 0, bytesRead);
                    }
                }
            } catch (IOException | SecurityException e) {
    
    
                Log.w(LOG_TAG, "Error staging apk from content URI", e);
                return false;
            }
            return true;
        }
        @Override
        protected void onPostExecute(Boolean success) {
    
    
           if (session != null) {
    
    
         Intent broadcastIntent = new Intent(BROADCAST_ACTION);
         broadcastIntent.setPackage(
                 getPackageManager().getPermissionControllerPackageName());
         broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);
         PendingIntent pendingIntent = PendingIntent.getBroadcast(
                 InstallInstalling.this,
                 mInstallId,
                 broadcastIntent,
                 PendingIntent.FLAG_UPDATE_CURRENT);
        //APP安装的启动入口
         session.commit(pendingIntent.getIntentSender());
         mCancelButton.setEnabled(false);
         setFinishOnTouchOutside(false);
     } else {
    
    
         getPackageManager().getPackageInstaller().abandonSession(mSessionId);
         if (!isCancelled()) {
    
    
             launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
         }
     }
}

4. Introduction to the PackageInstallerActivity class
It is the Activity that displays the pop-up window when installing the application

@Override
protected void onCreate(Bundle icicle) {
    
    
    super.onCreate(icicle);
    if (icicle != null) {
    
    
        mAllowUnknownSources = icicle.getBoolean(ALLOW_UNKNOWN_SOURCES_KEY);
    }
    mPm = getPackageManager();
    mIpm = AppGlobals.getPackageManager();
    mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
    mInstaller = mPm.getPackageInstaller();
    mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
    ...
    //根据Uri的Scheme进行预处理
    boolean wasSetUp = processPackageUri(packageUri);
    if (!wasSetUp) {
    
    
        return;
    }
    bindUi(R.layout.install_confirm, false);
    //判断是否是未知来源的应用,如果开启允许安装未知来源选项则直接初始化安装
    checkIfAllowedAndInitiateInstall();
}

Separately handle two different protocols of content and package

private boolean processPackageUri(final Uri packageUri) {
    
    
     mPackageURI = packageUri;
     final String scheme = packageUri.getScheme();//1
     switch (scheme) {
    
    
         case SCHEME_PACKAGE: {
    
    
             try {
    
    
              ...
         } break;
         case SCHEME_FILE: {
    
    
             File sourceFile = new File(packageUri.getPath());
             //得到sourceFile的包信息
             PackageParser.Package parsed = PackageUtil.getPackageInfo(this, 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;
             }
             //对parsed进行进一步处理得到包信息PackageInfo
             mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
                     PackageManager.GET_PERMISSIONS, 0, 0, null,
                     new PackageUserState());//3
             mAppSnippet = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
         } break;
         default: {
    
    
             Log.w(TAG, "Unsupported scheme " + scheme);
             setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
             finish();
             return false;
         }
     }
     return true;
 }

The pop-up window shows whether it is an illegal installation process

private void checkIfAllowedAndInitiateInstall() {
    
    
       //判断如果允许安装未知来源或者根据Intent判断得出该APK不是未知来源
       if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {
    
    
           //初始化安装
           initiateInstall();
           return;
       }
       // 如果管理员限制来自未知源的安装, 就弹出提示Dialog或者跳转到设置界面
       if (isUnknownSourcesDisallowed()) {
    
    
           if ((mUserManager.getUserRestrictionSource(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
                   Process.myUserHandle()) & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
    
        
               showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER);
               return;
           } else {
    
    
               startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
               finish();
           }
       } else {
    
    
           handleUnknownSources();
       }
   }

InstallStaging.java session.commit() to execute the system framework layer

 protected void onPostExecute(Boolean success) {
    
    
           if (session != null) {
    
    
         Intent broadcastIntent = new Intent(BROADCAST_ACTION);
         broadcastIntent.setPackage(
                 getPackageManager().getPermissionControllerPackageName());
         broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);
         PendingIntent pendingIntent = PendingIntent.getBroadcast(
                 InstallInstalling.this,
                 mInstallId,
                 broadcastIntent,
                 PendingIntent.FLAG_UPDATE_CURRENT);
        //APP安装的启动入口
         session.commit(pendingIntent.getIntentSender());
         mCancelButton.setEnabled(false);
         setFinishOnTouchOutside(false);
     } else {
    
    
         getPackageManager().getPackageInstaller().abandonSession(mSessionId);
         if (!isCancelled()) {
    
    
             launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
         }
     }

PackageInstaller.java class

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

PackageInstallerSession.java class
PackageInstallObserverAdapter inherits PackageInstallObserver: monitor the process of installing APP
mSessionId is the session id of the installation package, mInstallId is the waiting installation event id

@Override
   public void commit(IntentSender statusReceiver) {
    
    
       Preconditions.checkNotNull(statusReceiver);
       ...
       mActiveCount.incrementAndGet();
       final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
               statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);
      //Handler发送一个类型为MSG_COMMIT的消息,通知PMS安装应用
       mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
   }

private final Handler.Callback mHandlerCallback = new Handler.Callback() {
    
    
      @Override
      public boolean handleMessage(Message msg) {
    
    
          final PackageInfo pkgInfo = mPm.getPackageInfo(
                  params.appPackageName, PackageManager.GET_SIGNATURES
                          | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
          final ApplicationInfo appInfo = mPm.getApplicationInfo(
                  params.appPackageName, 0, userId);
          synchronized (mLock) {
    
    
              if (msg.obj != null) {
    
    
                  mRemoteObserver = (IPackageInstallObserver2) msg.obj;
              }
              try {
    
    
                  //PMS开始安装应用
                  commitLocked(pkgInfo, appInfo);
              } catch (PackageManagerException e) {
    
    
                  final String completeMsg = ExceptionUtils.getCompleteMessage(e);
                  Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
                  destroyInternal();
                   //安装时候出现异常问题
                  dispatchSessionFinished(e.error, completeMsg, null);
              }
              return true;
          }
      }
  };

private void commitLocked(PackageInfo pkgInfo, ApplicationInfo appInfo)
          throws PackageManagerException {
    
    
     ...
    //通知 PMS开始安装应用
      mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
              installerPackageName, installerUid, user, mCertificates);
  }

Summarize:

根据Uri的Scheme协议不同,跳转到不同的界面,content协议跳转到InstallStaging,package跳转到PackageInstallerActivity。
InstallStaging将content协议的Uri转换为File协议,然后跳转到PackageInstallerActivity。
PackageInstallerActivity会分别对package协议和file协议的Uri进行处理,如果是file协议会解析APK文件得到包信息PackageInfo。
PackageInstallerActivity中会对未知来源进行处理,如果允许安装未知来源或者根据Intent判断得出该APK不是未知来源,就会初始化安装确认界面,如果管理员限制来自未知源的安装, 就弹出提示Dialog或者跳转到设置界面。

Guess you like

Origin blog.csdn.net/qq_24252589/article/details/131353229