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:
- 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());
- 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);
-
Determine el protocolo de esquema de Uri, si es
content://
así, salteInstallStaging
, si espackage://
así, saltePackageInstallerActivity
, peroInstallStaging
en realidadStagingAsyncTask
, el Uri del protocolo de contenido se convertirá al protocolo de archivo y luego saltará aPackageInstallerActivity
-
finalizar
InstallStart
la interfaz actual
PackageInstallerActivity.java
El trabajo principal:
- Diálogo de instalación de confirmación de compilación
protected void onCreate(Bundle icicle) {
...
bindUi();
}
-
Varios objetos necesarios para la instalación inicial, como PackageManager, IPackageManager, AppOpsManager, UserManager, PackageInstaller, etc.
-
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);
- Ú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);
}
-
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". EnonActivityResult()
, llameinitiatenstall()
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. -
En la interfaz de instalación, haga clic en el botón Aceptar para confirmar la instalación y se le llamará para
startInstall
iniciar el trabajo de instalación. -
startInstall
El método se utiliza para saltarInstallInstalling
y cerrar la actualPackageInstallerActivity
InstalarInstalación.java
El trabajo principal:
-
Obtener y luego obtener los objetos
PackageManager
que contiene.PackageInstaller
-
PackageInstaller
Cree un paquete para transmitir el paquete de instalación de APK y la información de instalación ySession
devolverloSessionId
-
Cree una tarea asincrónica y use el extremo abierto devuelto anteriormente
InstallingAsyncTask
en la tarea asincrónicaSessionId
PackageInstaller
Session
-
Pasando
Session
el paquete de instalación APK y la información relacionada. -
onPostExecute
Ejecutado en método de tarea asincrónica.session.commit(pendingIntent.getIntentSender())
-
InstallEventReceiver
Escuche 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 PackageInstallerSession
servicio 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
IPackageInstallerSession
La 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
MSG_STREAM_VALIDATE_AND_COMMIT
mensaje 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;
}
};
- La ejecución final está
handleStreamValidateAndCommit()
en, y luego se envía un mensaje dentroMSG_INSTALL
, esta ejecución está enhandleInstall()
:
//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);
...
}
- Finalmente, el proceso de instalación llegó a su
PMS
fininstallStage()
.
Hasta ahora, PackageInstaller
solo se ha mantenido la instalación Session
y se ha escrito el paquete de instalación , y se ejecutará Session
el proceso de instalación real .PMS
PackageManagerService.java
el trabajo principal
installStage()
Se crea el objetoInstallParams
,message
se pasa el mensaje; el mensaje se envíaINIT_COPY
amHandler
.
//PMS
void installStage(ActiveInstallSession activeInstallSession) {
...
final Message msg = mHandler.obtainMessage(INIT_COPY);
final InstallParams params = new InstallParams(activeInstallSession);
msg.obj = params;
mHandler.sendMessage(msg);
}
mHandler
Sí PackageHandler
, esto se PackageManagerService
crea en el constructor.
mHandler
El procesamiento INIT_COPY
es 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;
}
...
}
}
HandlerParams
El método de se llama dentro startCopy()
. HandlerParams
Sí, PMS
clase abstracta interna. PMS
La clase interna InstallParams
es MultiPackageInstallParams
su 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
.
-
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.
-
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);
}
}
}
FileInstallArgs
Clase 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/app
directorio.
//拷贝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)
processPendingInstall()
Llame aprocessInstallRequestsAsync()
,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:
- 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);
- 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);
- 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 Installer
y Installer
se hereda de SystemService
tambié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);
...
}
mHandler
Sigue siendo PackageHandler
un objeto.
mHandler
Utilice handlePackagePostInstall()
el método PMS para procesar POST_INSTALL
:
- Envío
Intent.ACTION_PACKAGE_ADDED
y 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
PackageInstallSession
guardada 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.IPackageInstallObserver2
onPackageInstalled
IntentSender
InstallInstalling
InstallInstalling
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.