Proceso de visualización de la ventana emergente de instalación de la aplicación de Android

1. INSTALACIÓN DE LA APLICACIÓN

1. Métodos de instalación comunes
Aplicación del sistema e instalación de aplicaciones prefabricadas: se completa al iniciar, no hay una interfaz de instalación y la instalación se completa en el constructor de PKMS

Descarga de Internet o instalación de aplicaciones de terceros: llame a PackageManager.installPackages(), hay una interfaz de instalación.

Instalación de la herramienta ADB: no hay una interfaz de instalación, inicia el script pm, luego llama a la clase com.android.commands.pm.Pm y luego llama a PMS.installStage() para completar la instalación.
inserte la descripción de la imagen aquí2. Comprensión de verificación de firma APK

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

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

3. Proceso de instalación de APK

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

4. Comprensión de los directorios involucrados en la instalación de la aplicación
Directorio de instalación de la aplicación del sistema

1. /system/app: ruta de la aplicación del sistema Android
2. /system/priv-app: igual que arriba, pero con mayor prioridad que el permiso /system/app, puede obtener el permiso especial ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
3. /vendor/app: odm o sistema prefabricado OEM Directorio de aplicaciones
4, /vendor/priva-app: Igual que arriba

Aplicación ordinaria Directorio de instalación de la aplicación

/data/app: el directorio donde está instalado el programa de la aplicación del usuario. Apk se copiará en este directorio durante la instalación

directorio de datos de usuario

/data/data: almacena datos de la aplicación, ya sea una aplicación del sistema o una aplicación normal, los datos de usuario generados por la aplicación se almacenan en el directorio /data/data/package name/.

Directorio de registro de aplicaciones

/data/system
1. packages.xml:
registra los permisos, flags, ts, versión, uesrid y otra información del apk. Esta información se obtiene principalmente analizando el AndroidManifest.xml del apk. Cuando el sistema realiza operaciones como la instalación, desinstalación y actualización del programa actualizará el archivo.
2. packages-backup.xml: archivo de copia de seguridad
3. packages-stopped.xml: registra la información del paquete de la aplicación que el usuario detuvo a la fuerza
4. packages-stopped-backup.xml: copia de seguridad de pakcages-stopped.xml file
5. packages.list: registre la información de datos de los APK que no están incluidos en el sistema. Cuando estos APK cambien, el archivo se actualizará

5. Análisis del archivo package.xml

<?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>   


inserte la descripción de la imagen aquíLa relación del diagrama de clases BasePermission correspondiente a package.xml

BasePermission corresponde al elemento de subetiqueta de la etiqueta de permisos en packages.xml, y se generará un BasePermission para cada permiso definido anteriormente.
protección: Hay cuatro niveles:
1. Autoridad normal (normal)
2. Autoridad de tiempo de ejecución (peligroso)
3. Autoridad de firma (firma)
4. Autoridad especial (privilegiado)

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

PermisosEstado
inserte la descripción de la imagen aquí

PermissionState corresponde al contenido en la etiqueta de subetiqueta en la etiqueta

<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>


PaqueteFirmas

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>


Configuración del paquete

La clase de estructura de datos PackageSetting es la clase correspondiente a la etiqueta de información del paquete de instalación registrada en packages.xml.Puede ver que PackageSetting hereda la clase PackageSettingBase y la clase PackageSettingBase hereda de la clase SettingBase. La información básica de la aplicación se almacena en las variables miembro de la clase PackageSettingBase, la firma se almacena en PackageSignatures y el estado del permiso se almacena en PermissionsState de la clase principal 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>  

Configuración de usuario compartido

La clase de estructura de datos SharedUserSetting es la clase correspondiente a la etiqueta de información del paquete de instalación registrado en packages.xml Tiene una clase padre común, SettingBase, con PackageSetting, es decir, la información de permisos se guarda a través del PermissionsState de la clase padre . SharedUserSetting está diseñado para describir la información de la aplicación con el mismo sharedUserId. Sus paquetes de variables miembro guardan todas las referencias de información de la aplicación con el mismo sharedUserId, y la variable miembro userId registra el UID compartido por varios APK. Las firmas de las aplicaciones que comparten usuarios son las mismas, y las firmas se almacenan en las firmas de variables miembro (una cosa a tener en cuenta aquí, debido a que las firmas son las mismas, es fácil para el tiempo de ejecución de Android recuperar otras aplicaciones que tienen el mismo sharedUserId para una aplicación).

inserte la descripción de la imagen aquí

 <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>

Configuraciones: paquete.xml última clase de ama de llaves
inserte la descripción de la imagen aquí

2. El proceso general de instalación de la aplicación

Repositorio de código: http://androidxref.com/9.0.0_r3/xref/packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallStart.java

1. Instale la entrada del código de la aplicación

    <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. De acuerdo con los diferentes protocolos de esquema de Uri, salte a diferentes interfaces.
El protocolo de contenido salta a InstallStaging y el protocolo de paquete salta a 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. Introducción a la clase InstallStaging

Contenido principal: Convierta el Uri del protocolo de contenido al Uri del protocolo del paquete y luego escríbalo en el archivo mStagedFile a través de IO.Función
: Desempeña principalmente el papel de conversión, convierte el Uri del protocolo de contenido al protocolo del paquete y luego salta a 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. Introducción a la clase PackageInstallerActivity
Es la Actividad que muestra la ventana emergente al instalar la aplicación

@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();
}

Manejar por separado dos protocolos diferentes de contenido y paquete

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;
 }

La ventana emergente muestra si se trata de un proceso de instalación ilegal

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() para ejecutar la capa del marco del sistema

 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);
         }
     }

Clase PackageInstaller.java

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

PackageInstallerSession.java clase
PackageInstallObserverAdapter hereda PackageInstallObserver: monitorear el proceso de instalación de la aplicación
mSessionId es la identificación de la sesión del paquete de instalación, mInstallId es la identificación del evento de instalación en espera

@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);
  }

Resumir:

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

Supongo que te gusta

Origin blog.csdn.net/qq_24252589/article/details/131353229
Recomendado
Clasificación