Android crea aplicaciones a nivel de sistema con autorización silenciosa e instalación silenciosa

fondo

El desarrollo de aplicaciones basadas en un sistema de tableta personalizado puede obtener el soporte de la capa de código fuente de Android.
Sistema Android8.1.

Objetivo

  • De forma predeterminada, se obtienen todos los permisos necesarios y no se solicita autorización en ninguna ventana emergente.
  • Una actualización silenciosa instala una nueva versión.

lograr

Obtener permisos de aplicación del sistema

A través de sharedUserIdesta configuración, el proceso de aplicación actual se configura como un proceso de aplicación del sistema y se pueden obtener todos los permisos.

principio

A través de la identificación de usuario compartida, se pueden configurar múltiples APK con la misma identificación de usuario para que se ejecuten en el mismo proceso, por lo que, de forma predeterminada, pueden acceder a cualquier información entre sí y, al mismo tiempo, pueden acceder a bases de datos y archivos en el directorio de datos. de otros APK, al igual que acceder a los datos de este programa es lo mismo.

Para evitar que otras aplicaciones obtengan el ID de usuario compartido y abusen de él, el sistema Android restringe que el mismo ID de usuario debe usar la misma firma para instalarse en el mismo dispositivo. Por lo tanto, si desea configurarlo como un proceso compartido del sistema, debe ser coherente con la firma de la aplicación del sistema.

La lógica para que el sistema firme la aplicación:

build/target/product/securityHay cuatro conjuntos de firmas predeterminadas en el directorio del código fuente del sistema Android.mkpara usar en la compilación del APK, que LOCAL_CERTIFICATEse implementan especificando campos, que son:

  • testkey: APK normal, usado por defecto.
  • platform: El APK completa algunas funciones básicas del sistema. Luego de la prueba de acceso a las carpetas existentes en el sistema, el UID del proceso donde se compila el APK de esta forma es sistema.
  • shared: El APK necesita compartir datos con el proceso de inicio/contactos.
  • media: El APK es parte del sistema de medios/descargas

Es utilizado por aplicaciones ordinarias.Si testkeyes una aplicación del sistema, se puede especificar como platform.

El principio y la introducción se refieren a este artículo : Mecanismo de permisos de Android: mecanismo de "caja de arena" sharedUserId y firma

lograr

Consulte este artículo para implementar : Firma del sistema Android apk

1. Requisito previo:
la firma de la aplicación no puede ser una firma personalizada, pero debe ser una firma del sistema. El proveedor del sistema debe proporcionar el archivo de firma, incluidos varios archivos, como platform.pk8, platform.x509.pem, signapk.jaretc.. Si no se puede proporcionar este paso, no sharedUserIdhay solución a través de la configuración.

2. ConfigurarsharedUserId el aumento de nodos
en :AndroidManifest.xmlmanifest

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.moore.systemapk"
    android:sharedUserId="android.uid.system">
</manifest>

3.
Método de firma 1:
en la capa de código fuente del sistema, configure la aplicación como Android.mkel tipo de aplicación del sistema entre los cuatro tipos predeterminados en el principio correspondienteLOCAL_CERTIFICATEplatform

Método 2:
cree el paquete apk sin firmar y firme la aplicación a través del método de comando.
java -jar signapk.jar platform.x509.pem platform.pk8 unsigned.apk signed.apk
Reemplace su nombre y ruta de apk unsigned.apkcon signed.apkla ruta de salida.

Método 3:
según el archivo de firma proporcionado por el sistema, genere un archivo jks o keystone, que se puede empaquetar y firmar directamente en el AS sin firma adicional.

  1. generar shared.priv.pemarchivo

openssl pkcs8 -in platform.pk8 -inform DER -outform PEM -out shared.priv.pem -nocrypt

  1. generar shared.pk12archivo

openssl pkcs12 -export -in platform.x509.pem -inkey shared.priv.pem -out shared.pk12 -name moore

  1. generar jkso keystonearchivar

keytool -importkeystore -deststorepass android -destkeypass android -destkeystore moore.jks -srckeystore shared.pk12 -srcstoretype PKCS12 -srcstorepass android -alias moore

Después de la generación, se puede configurar en gradle, o elegir la ruta donde se encuentra la firma al empaquetar

4. Problemas
En el proceso de operación real, si usa el método de comando para firmar, existe una alta probabilidad no conscrypt_openjdk_jni in java.library.path:de errores. Obtenga el proporcionado por el fabricante libconscrypt_openjdk_jni.so, vuelva a firmar e informe otro error: FATAL ERROR in native method: RegisterNatives failed for 'org/conscrypt/NativeCrypto'intente cambiar la computadora o system (windows, mac, linux) No hay solución Es normal que la computadora del lado del fabricante ejecute el comando, así que me rendí y lo ejecuté generando una firma jks.

5. Instalación
Dado que se ha cambiado la firma, si la aplicación está instalada en el dispositivo donde se instaló originalmente, debe desinstalarse antes. El mismo nombre de paquete tiene una firma diferente, y la instalación obviamente no se realizó correctamente.

instalación silenciosa

La aplicación realiza la función de actualización automática y existen varios métodos de instalación:

  • Llame al instalador del sistema y pase la ruta apk (se requiere autorización para permitir la instalación de aplicaciones)
  • Ejecute el comando de shell para instalar silenciosamente apk (requiere autoridad de root)
  • Use PackageInstallerel código para instalar, simule el proceso de instalación del sistema (debe ser una aplicación del sistema)
Instalación de actualización normal

En primer lugar, la página de instalación aparecerá de esta manera, lo que no satisface nuestras necesidades, pero se encontraron algunos problemas durante el proceso de instalación, por lo que los enumeraremos para futuras referencias.

1. Método
Después de descargar el apk del paquete de actualización, use directamente Intent para invocar la intención de instalación del sistema. Cabe señalar que se requiere personalización para las versiones superiores a la 8.0 FileProvider, de lo contrario, no se puede obtener la ruta y se generará una excepción FileUriExposedException.

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri fileUri = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    //自定义的FileProvider,需要在清单文件中声明
    String authority = XUpdate.getContext().getPackageName() + ".updateFileProvider";
    fileUri = FileProvider.getUriForFile(XUpdate.getContext(), authority, file);
} else {
    fileUri = Uri.fromFile(file);
}
intent.setDataAndType(fileUri, "application/vnd.android.package-archive");
context.startActivity(intent)

2. Problema
Aquí viene el problema, de acuerdo con el proceso de aplicación normal, este método no es un problema, todo es normal, pero nuestra aplicación se ha configurado ** android:sharedUserId="android.uid.system"**, es un poco problemático.

Al iniciar la instalación, se informa un error: hubo un problema al analizar el paquete

He confirmado que no hay ningún problema con el paquete de instalación descargado. La instalación se puede iniciar a través de la administración de archivos y otras aplicaciones, y se puede instalar normalmente después de eliminar el systemUid. El problema es el sharedUserId.

Motivo:
Al rastrear el programa fuente del proceso de instalación del sistema, se encontró el siguiente código:

final int callingAppId = UserHandle.getAppId(callingUid);
if ((callingAppId == SYSTEM_UID) || (callingAppId == ROOT_UID)) {
    if ("com.android.settings.files".equals(grantUri.uri.getAuthority())) {
        // Exempted authority for cropping user photos in Settings app
    } else {
        Slog.w(TAG, "For security reasons, the system cannot issue a Uri permission"
                + " grant to " + grantUri + "; use startActivityAsCaller() instead");
        return -1;
    }
}

ok, para solucionar el caso, porque somos una aplicación configurada en SystemUid, pero nuestro proveedor no lo es com.android.settings.files, por lo que directamente está bloqueado return -1, y com.android.settings.fileseste proveedor está definido en la aplicación del sistema de Configuración, es decir, solo la aplicación SystemUid de Configuración can Invoca el proceso de instalación normal.

solución:

  • No podemos cambiar el nuestro al android:authoritiesdisponible arriba com.android.settings.files, porque este es el único valor en todo el sistema, y ​​no se puede instalar en el dispositivo si se cambia al mismo valor. empeñar.
  • Elimine systemUid, se puede instalar normalmente, si puede eliminarlo, no se molestará en estudiarlo. empeñar.
  • ¡Modifica el código fuente! En el juicio anterior, agregue una condición o, agregue la nuestra authorities, pero si desea modificar el código fuente, puede usarlo condicionalmente

Para obtener un análisis detallado de este problema, consulte el artículo : La llamada de aplicación de Android android.uid.system no puede instalar apk

instalación de comandos de shell

Como sugiere el nombre, se instala directamente ingresando comandos para ejecutar. Todos sabemos que el kernel de Android es Linux, por lo que el principio es usar la cuenta raíz para comandar directamente la instalación.
Primero debe determinar si tiene privilegios de root:

public static boolean isDeviceRooted() {
    String su = "su";
    String[] locations = {"/system/bin/", "/system/xbin/", "/sbin/", "/system/sd/xbin/",
            "/system/bin/failsafe/", "/data/local/xbin/", "/data/local/bin/", "/data/local/"};
    for (String location : locations) {
        if (new File(location + su).exists()) {
            return true;
        }
    }
    return false;
}

Si tiene permiso, el comando de empalme filePathes la ruta apk

String command = "pm install -i " + packageName + " --user 0 " + filePath;

Luego ejecute el comando a través del método de ShellUtils .execCommand

Si tiene permiso de root, este método es factible y no habrá interfaz, pero los dispositivos que lanzamos al mundo exterior no abrirán el permiso de root, por lo que este método solo se puede usar durante el desarrollo y las pruebas, y no se puede operar en línea. .

Simular el flujo del instalador del sistema

Antes de la introducción, puede consultar estos dos artículos para comprender el proceso de la aplicación de instalación del sistema y la lógica del código
Mecanismo de administración de paquetes de Android (1) Inicialización de PackageInstaller
Mecanismo de administración de paquetes de Android (2) APK de instalación de PackageInstaller

Lo que tenemos que hacer es analizar y llamar a la página de instalación del sistema, qué operaciones hace la capa lógica después de hacer clic en instalar, y luego implementar este proceso directamente en nuestra aplicación.

flujo general

  1. Defina una devolución de llamada de escucha para recibir notificaciones sobre el inicio, el progreso y el final de la instalación
  2. Obtenga el objeto de PackageManagerGet PackageInstaller, obtenga el archivo apk Objeto de archivo
  3. Cree una sesión de instalación Sessiony obtenga la identificación de la sesión
  4. Lea el archivo apk y escriba el archivo como una secuenciasession
  5. Cree uno PendingIntentpara invocar la lógica de instalación de PMS
  6. Confirme esta sesión e ingrese a la instalación de PMS

Introducción a la instalación de PMS:

  1. Después de recibir la confirmación de la sesión, se ensamblará en un PackageInstallObserverAdapterobjeto, Handlerque será procesado por PMS enviando el mensaje
  2. PackageManagerServiceLa lógica de instalación PMSHandlerestá IMediaContainerServicerelacionada con varias clases.
  3. PMSHandlerProcese la lógica de los mensajes de vinculación, los nuevos mensajes de instalación, etc. Solo cuando la vinculación se realiza correctamente se puede realizar la siguiente operación de instalación.
  4. Vincule el servicio a través de la carpeta, analice y copie el apk (operación que consume mucho tiempo, abra un nuevo proceso de servicio en el sistema Android)
  5. InstallParamsY InstallArgsoperaciones específicas como inspección previa a la instalación, confirmación del lugar de instalación, copiar y mover, etc.
  6. Una vez que la instalación es exitosa, se distribuye un mensaje a través del PMS para notificar al usuario de la aplicación, actualizar la configuración y preparar los datos para la aplicación recién instalada. Si la instalación falla, el apk se eliminará.
  7. desconectar la carpeta

Artículo de referencia: : Mecanismo de administración de paquetes de Android (3) PMS maneja la instalación de APK

clave

int count;
int sessionId;
byte[] buffer = new byte[65536];
InputStream inputStream;
OutputStream outputStream;
//拿到apkFile,拿到packageInstaller对象
File apkFile = new File(apkFilePath);
long apkFileLength = apkFile.length();
PackageManager pm = context.getPackageManager();
PackageInstaller packageInstaller = pm.getPackageInstaller();
packageInstaller.registerSessionCallback(忽略回调实现);
//创建会话
PackageInstaller.Session session = null;
PackageInstaller.SessionParams sessionParams;
try {
    sessionParams = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
    sessionId = packageInstaller.createSession(sessionParams);
    session = packageInstaller.openSession(sessionId);
    inputStream = new FileInputStream(apkFile);
    outputStream = session.openWrite(apkName, 0, apkFileLength);
    //写入
    while ((count = inputStream.read(buffer)) != -1) {
        outputStream.write(buffer, 0, count);
        float progress = ((float) count / (float) apkFileLength);
        session.setStagingProgress(progress);
    }
    session.fsync(outputStream);
    inputStream.close();
    outputStream.flush();
    outputStream.close();
    //创建PendingIntent
    Intent intent = new Intent();
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
    //绑定并提交
    session.commit(pendingIntent.getIntentSender());
} catch (Exception e) {
    e.printStackTrace();
} finally {
    if (session != null) {
        session.abandon();
    }
}

En este punto, la instalación silenciosa se puede realizar, pero todavía hay un problema. Normalmente, nuestra actualización del sistema necesita reiniciar la aplicación después de que se complete la actualización. Sin embargo, debido a que nuestra aplicación se elimina durante el proceso de instalación, por lo tanto, la devolución de llamada configurado al crear la sesión no puede recibir la devolución de llamada para completar la instalación. !

La instalación está completa y reinicia.

Idea:
al comienzo de la instalación, cree una tarea de AlarmManager y vuelva a abrir la aplicación después de 10 segundos (dependiendo del tamaño del apk).

Intent intent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
PendingIntent restartIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {// 6.0及以上
    mgr.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 10000, restartIntent);
}

De esta manera, se puede lograr una instalación silenciosa y la aplicación se reiniciará una vez que se complete la instalación.

Supongo que te gusta

Origin blog.csdn.net/lizebin_bin/article/details/125293599
Recomendado
Clasificación