Análisis del proceso de instalación de APK.

El código para iniciar la instalación de APK en el lado de la aplicación es generalmente el siguiente:

 Intent installintent = new Intent();
 installintent.setAction(Intent.ACTION_VIEW);
 installintent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 installintent.setDataAndType(xxx,"application/vnd.android.package-archive");
 context.startActivity(installintent);

La instalación del apk se realiza esencialmente a través del paquete de aplicaciones del sistemaInstaller.apk. Por lo tanto, lo que debemos mirar es el código fuente de packageInstaller. Abra el archivo AndroidManifest.xml de PackageInstaller, encontraremos que la actividad que iniciará el Intent anterior coincide con InstallStart, que también es la entrada de la aplicación PackageInstaller:

<activity android:name=".InstallStart"
        android:theme="@android:style/Theme.Translucent.NoTitleBar"
        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="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="package"/>
            <data android:scheme="content"/>
        </intent-filter>
        
        <intent-filter android:priority="1">
            <action android:name="android.content.pm.action.CONFIRM_INSTALL"/>
            <category android:name="android.intent.category.DEFAULT"/>
        </intent-filter>
    </activity>

InstalarInicio.java

/frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java

Saltar a InstallStart.java

El trabajo principal:

  1. Determine si es necesario mostrar un cuadro de diálogo para confirmar la instalación. Si es así, salte PackageInstallerActivity; si no, salte a 3
final boolean isSessionInstall =PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction());
  1. Determinar si la opción "Confiar en fuentes desconocidas" está marcada. Si no está marcada, determinar si la versión es menor que Android 8.0 y, de ser así, cancelar la instalación; en caso contrario, determinar si la versión es mayor que Android 8.0 y no tiene permiso para configurar, si es así, cancele la instalación REQUEST_INSTALL_PACKAGES.
isTrustedSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false);
  1. Determine el protocolo de esquema de Uri, si es content://así, salte InstallStaging, si es package://así, salte PackageInstallerActivity, pero InstallStagingen realidad StagingAsyncTask, el Uri del protocolo de contenido se convertirá al protocolo de archivo y luego saltará aPackageInstallerActivity

  2. finalizar InstallStartla interfaz actual

PackageInstallerActivity.java

El trabajo principal:

  1. Diálogo de instalación de confirmación de compilación
protected void onCreate(Bundle icicle) {
	...
	bindUi();
}
  1. Varios objetos necesarios para la instalación inicial, como PackageManager, IPackageManager, AppOpsManager, UserManager, PackageInstaller, etc.

  2. Se realizan diferentes procesamientos de acuerdo con el protocolo de esquema pasado (paquete / archivo), principalmente para obtener el objeto PackageInfo. PackageInfo contiene información sobre la aplicación de Android, como el nombre de la aplicación, la lista de permisos, etc., y crea un objeto AppSnippet a través de PackageInfo. que contiene el icono de la aplicación a instalar y el título

 boolean wasSetUp = processPackageUri(packageUri);
  1. Úselo checkIfAllowedAndInitiateInstall()para verificar la fuente del APK y mostrar el cuadro de diálogo "Instalación de APK de fuente desconocida". Cuando haga clic en el botón "configuración", saltará a la página de configuración.
  Intent settingsIntent = new Intent();
    settingsIntent.setAction(
    Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
    Uri packageUri = Uri.parse("package:" + argument);
    settingsIntent.setData(packageUri);
    try{
        getActivity().startActivityForResult(settingsIntent, REQUEST_TRUST_EXTERNAL_SOURCE);
    } catch(ActivityNotFoundException exc){
        Log.e(TAG, "Settings activity not found for action: " + Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
    }
  1. Después de activar la opción de configuración "Permitir instalación desde fuentes desconocidas" PackageInstallerActivity, regrese a la opción de configuración "Permitir instalación desde fuentes desconocidas". En onActivityResult(), llame initiatenstall()a la lista de aplicaciones de verificación para determinar si la aplicación se ha instalado. Si está instalada, se le indicará que la aplicación se ha instalado, el usuario decidirá si la reemplaza y luego mostrará la interfaz de confirmación de instalación.

  2. En la interfaz de instalación, haga clic en el botón Aceptar para confirmar la instalación y se le llamará para startInstalliniciar el trabajo de instalación.

  3. startInstallEl método se utiliza para saltar InstallInstallingy cerrar la actualPackageInstallerActivity

InstalarInstalación.java

El trabajo principal:

  1. Obtener y luego obtener los objetos PackageManagerque contiene.PackageInstaller

  2. PackageInstallerCree un paquete para transmitir el paquete de instalación de APK y la información de instalación y SessiondevolverloSessionId

  3. Cree una tarea asincrónica y use el extremo abierto devuelto anteriormente InstallingAsyncTasken la tarea asincrónicaSessionIdPackageInstallerSession

  4. Pasando Sessionel paquete de instalación APK y la información relacionada.

  5. onPostExecuteEjecutado en método de tarea asincrónica.session.commit(pendingIntent.getIntentSender())

  6. InstallEventReceiverEscuche las devoluciones de llamadas de instalaciones exitosas y fallidas registrando observadores y salte a la página de resultados correspondiente.

     <receiver android:name=".InstallEventReceiver"
        android:permission="android.permission.INSTALL_PACKAGES"
        android:exported="true">
            <intent-filter android:priority="1">
                <action android:name="com.android.packageinstaller.ACTION_INSTALL_COMMIT" />
            </intent-filter>
     </receiver>

Sesión del instalador del paquete

session.commit()El método se transfiere al PackageInstallerSessionservicio a través de una carpeta entre procesos.

La interfaz definida a través del archivo Aidl es:

/frameworks/base/core/java/android/content/pm/IPackageInstallerSession.aidl

IPackageInstallerSessionLa clase de implementación es:

/frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
//PackageInstallerSession.java
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
    ...
    dispatchStreamValidateAndCommit();
}

private void dispatchStreamValidateAndCommit() {
    mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
}

el trabajo principal

  1. MSG_STREAM_VALIDATE_AND_COMMITmensaje enviado amHandler
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_STREAM_VALIDATE_AND_COMMIT:
                handleStreamValidateAndCommit(); //
                break;
            case MSG_INSTALL:
                handleInstall(); //
                break;
            ...
        }
        return true;
    }
};
  1. La ejecución final está handleStreamValidateAndCommit()en, y luego se envía un mensaje dentro MSG_INSTALL, esta ejecución está en handleInstall():
//PackageInstallerSession.java
    public void handleStreamValidateAndCommit() {
        ...
         mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
    }
    rivate final Handler.Callback mHandlerCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_STREAM_VALIDATE_AND_COMMIT:
                    handleStreamValidateAndCommit(); //
                    break;
                case MSG_INSTALL:
                    handleInstall(); //
                    break;
                ...
            }
            return true;
        }
    };

    private void handleInstall() {
        ...
        List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();

        installNonStagedLocked(childSessions);
        ...
    }

    private void installNonStagedLocked(List<PackageInstallerSession> childSessions) {
        final PackageManagerService.ActiveInstallSession installingSession = makeSessionActiveLocked();
         ...
         //安装走到了 PMS 中
         mPm.installStage(installingSession);
      ...
    }
  1. Finalmente, el proceso de instalación llegó a su PMSfin installStage().

Hasta ahora, PackageInstallersolo se ha mantenido la instalación Sessiony se ha escrito el paquete de instalación , y se ejecutará Sessionel proceso de instalación real .PMS

PackageManagerService.java

el trabajo principal

  1. installStage()Se crea el objeto InstallParams, messagese pasa el mensaje; el mensaje se envía INIT_COPYa mHandler.
//PMS
void installStage(ActiveInstallSession activeInstallSession) {
    ...
    final Message msg = mHandler.obtainMessage(INIT_COPY);
    final InstallParams params = new InstallParams(activeInstallSession);
    msg.obj = params;
    mHandler.sendMessage(msg);
}

mHandlerPackageHandler, esto se PackageManagerServicecrea en el constructor.
mHandlerEl procesamiento INIT_COPYes InstallParams.startCopy():

void doHandleMessage(Message msg) {
        switch (msg.what) {
            case INIT_COPY: {
                // 得到安装参数  HandlerParams 
                HandlerParams params = (HandlerParams) msg.obj;
                if (params != null) {
                    if (DEBUG_INSTALL) Slog.i(TAG, "init_copy: " + params);
                    Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                            System.identityHashCode(params));
                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
                    // 2 调用  startCopy() 方法
                    params.startCopy();
                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                }
                break;
            }
            ...
        } 
}          

HandlerParamsEl método de se llama dentro startCopy(). HandlerParamsSí, PMSclase abstracta interna. PMSLa clase interna InstallParamses MultiPackageInstallParamssu clase de implementación.

//HandlerParams.java
final void startCopy() {
    handleStartCopy();
    handleReturnCode();
}

handleStartCopy()、handleReturnCode()Todos ellos son métodos abstractos. Entonces, ¿cuál es el objeto aquí? Tras un análisis previo, sabemos que así es InstallParams.

  1. handleStartCopy() escanea la información ligera del apk, el espacio de instalación, la verificación de la integridad del apk, etc. Cree el objeto FileInstallArgs, asigne valor y código de retorno.

  2. handleReturnCode() primero llama al método copyApk() de InstallArgs para copiar el APK y luego lo instala;

  void handleReturnCode() {
            if (mVerificationCompleted && mIntegrityVerificationCompleted && mEnableRollbackCompleted) {
                ...
                //如果前面校验ok,这里执行apk拷贝
                if (mRet == PackageManager.INSTALL_SUCCEEDED) {
                    mRet = mArgs.copyApk();
                }
                //执行安装
                processPendingInstall(mArgs, mRet);
            }
        }
    }

FileInstallArgsClase heredada InstallArgs. copyApk()Llamado internamente doCopyApk();

doCopyApk() primero obtiene la ruta del archivo de copia: /data/app , usa PackageManagerServiceUtils.copyPackage() para copiar el APK y luego copia el archivo .so. En otras palabras, el APK enviado al directorio de preparación de la sesión data/app-staging se copia en /data/app.

/data/app-stagging Copie el apk del directorio temporal al /data/appdirectorio.

//拷贝apk
    int ret = PackageManagerServiceUtils.copyPackage(
            origin.file.getAbsolutePath(), codeFile);
    if (ret != PackageManager.INSTALL_SUCCEEDED) {
        Slog.e(TAG, "Failed to copy package");
        return ret;
    }

Siguiente vistazo a la ejecución de la instalación.processPendingInstall(mArgs, mRet)

  1. processPendingInstall()Llame a processInstallRequestsAsync(), processInstallRequestsAsync()realice la verificación previa a la instalación, comience a instalar el APK y envíe una transmisión notificando el resultado de la instalación.
  private void processInstallRequestsAsync(boolean success, List<InstallRequest>installRequests) {
        mHandler.post(() -> {
            if (success) {
                for (InstallRequest request : installRequests) {
                    //安装前检验:returnCode不为INSTALL_SUCCEEDED就移除拷贝的apk等
                    request.args.doPreInstall(request.installResult.returnCode);
                }
                synchronized (mInstallLock) {
                    //安装:解析apk
                    installPackagesTracedLI(installRequests);
                }
                for (InstallRequest request : installRequests) {
                    //同安装前检验
                    request.args.doPostInstall(request.installResult.returnCode, request.installResult.uid);
                }
            }
            for (InstallRequest request : installRequests) {
                //安装后续:备份、可能的回滚、发送安装完成先关广播
                restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult, new PostInstallData(request.args, request.installResult, null));
            }
        });
    }

Aquí primero nos centramos en el proceso de instalación y installPackagesTracedLI()luego vamos a installPackagesLI():

El proceso de instalación se divide en cuatro etapas:

  1. Prepare , analice el estado de instalación actual, analice el paquete y la verificación inicial: use PackageParser2.parsePackage() en preparePackageLI() para analizar AndroidManifest.xml para obtener los cuatro componentes principales y otra información; use ParsingPackageUtils.getSigningDetails() para analizar la información de la firma ; cambiar el nombre de la ruta final del paquete, etc.
    prepareResult = preparePackageLI(request.args, request.installResult);
  1. Escanee y analice más en función del contexto de la información del paquete analizada en la etapa de preparación: confirme la autenticidad del nombre del paquete; verifique la validez del paquete en función de la información analizada (si hay información de firma, etc.); recopile apk información: Configuración del paquete, biblioteca estática/biblioteca dinámica de apk, información, etc.
    final List<ScanResult> scanResults = scanPackageTracedLI( prepareResult.packageToScan, prepareResult.parseFlags, prepareResult.scanFlags, System.currentTimeMillis(), request.args.user);
  1. Verifique y verifique la información del paquete escaneado para garantizar una instalación exitosa: cubre principalmente la verificación de coincidencia de firmas de la instalación.
    reconciledPackages = reconcilePackagesLocked( reconcileRequest, mSettings.mKeySetManagerService);

4. Envíe , envíe el paquete escaneado y actualice el estado del sistema: agregue PackageSetting a mSettings de PMS, agregue AndroidPackage a mPackages de PMS, agregue un conjunto de claves al sistema, agregue permisos de aplicaciones a mPermissionManager y agregue información de cuatro componentes principales a mComponentResolver . Este es el único lugar donde se puede modificar el estado del sistema y se detectan todos los errores predecibles.
commitRequest = new CommitRequest(reconciledPackages, mUserManager.getUserIds()); commitPackagesLocked(commitRequest);

Una vez completada la instalación, se llama para executePostCommitSteps()preparar los datos de la aplicación y realizar la optimización dex:

  • prepareAppDataAfterInstallLIF(): Proporcionar estructura de directorio /data/user/用户ID/包名/cache(/datos/usuario/ID de usuario/nombre del paquete/code_cache)
  • mPackageDexOptimizer.performDexOpt(): dexopt es la operación de verificación y optimización del archivo dex. El resultado de la optimización del archivo dex se convierte en un archivo odex. Este archivo es muy similar al archivo dex, pero utiliza algunos códigos de operación de optimización (como optimización de instrucciones virtuales, etc.) .

Estas dos operaciones finalmente se realizan utilizando los métodos correspondientes de Installer . Como se mencionó anteriormente, la instancia de se pasó cuando se creó PMS Installery Installerse hereda de SystemServicetambién un servicio del sistema. Échale un vistazo aquí:

  • En el método onStart() del instalador, la instancia de mInstalld se obtiene a través del objeto Binder de installd. Se puede ver que esta es una operación de IPC, es decir, el instalador IPC en el proceso System_Server al proceso demonio con privilegios de root . Directorios como /data/user deben crearse con permisos de root.
  • En PMS se utilizan muchos métodos de Installer: Installer es la interfaz API de Java proporcionada por la capa Java e Installd es el proceso Daemon con permisos de root iniciado en el proceso de inicio.

Una vez completada la instalación aquí, regrese a PMS processInstallRequestsAsync()y finalmente solicite restoreAndPostInstall()una copia de seguridad, una posible reversión y envíe primero la finalización de la instalación para apagar la transmisión:

private void restoreAndPostInstall(
        int userId, PackageInstalledInfo res, @Nullable PostInstallData data) {
    ...
    Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
    mHandler.sendMessage(msg);
    ...
}

mHandlerSigue siendo PackageHandlerun objeto.

mHandlerUtilice handlePackagePostInstall()el método PMS para procesar POST_INSTALL:

  • Envío Intent.ACTION_PACKAGE_ADDEDy otras transmisiones basadas en los resultados de la instalación. Después de recibir la transmisión, el iniciador del escritorio agregará el ícono de la aplicación al escritorio.
  • Llame al método () de la instancia PackageInstallSessionguardada en y finalmente envíe una notificación de instalación exitosa para que se muestre en la barra de notificaciones. Envíe la transmisión definida en y finalmente la página muestra instalación exitosa/instalación fallida según los resultados.IPackageInstallObserver2onPackageInstalledIntentSenderInstallInstallingInstallInstalling

En este punto, el proceso de instalación del APK ya está resuelto, repasemos:

  • El APK se escribe en la sesión y la información del paquete y la operación de instalación del APK se envían al PMS;
  • En PMS, primero copie el APK en /data/app, luego use PackageParser2 para analizar el APK y obtener los cuatro componentes principales, recopilar firmas, PackageSetting y otra información, y realizar la verificación para garantizar una instalación exitosa;
  • Luego envíe el paquete de información para actualizar el estado del sistema y los datos de la memoria PMS;
  • Luego use el instalador para preparar el directorio de usuarios /data/user y realice dexOpt;
  • Finalmente, el resultado de la instalación se envía para notificar a la capa UI.

Supongo que te gusta

Origin blog.csdn.net/jxq1994/article/details/132875496
Recomendado
Clasificación