【Manuscrito de video】Desarrollo y análisis de la aplicación del sistema de desarrollo de aplicaciones Android para automóviles

Dirección de video de este número: https://www.bilibili.com/video/BV1NY411z7TK/

prefacio

Hola a todos, soy Lin Xu.

El desarrollo de aplicaciones en el vehículo es principalmente para escribir varias aplicaciones del sistema en el sistema Android, por lo que el video anterior presentó por primera vez el proceso de descarga y compilación del código fuente del sistema Android. En este video, comenzamos a presentar cómo son las aplicaciones del sistema Android. desarrollado.

Introducción a la aplicación del sistema

Cuando iniciamos el teléfono móvil con sistema Android por primera vez, nos encontraremos con que muchas aplicaciones han sido preinstaladas en el teléfono móvil, tales como: configuración del sistema, escritorio, etc. Estas aplicaciones no se instalan en el sistema a través de métodos ordinarios, sino que se integran directamente en la ROM de Android y se envían directamente al hardware. Las aplicaciones instaladas de esta manera no se pueden desinstalar de la forma habitual. Solo después de obtener el permiso de root, elimine el archivo apk (o flash) en el directorio correspondiente; de ​​lo contrario, estas aplicaciones del sistema no se pueden eliminar.

Además, también encontraremos que las aplicaciones del sistema tienen muchos más permisos que las aplicaciones ordinarias. Tomando la configuración del sistema como ejemplo, puede cambiar el tipo de usuario del sistema actual, establecer permisos de notificación para otras aplicaciones e incluso desinstalar aplicaciones ordinarias en el Aplicación del sistema Android, estas funciones no pueden ser realizadas por aplicaciones ordinarias, porque hay muchas API no publicadas en el SDK de Android, y estas API solo pueden ser llamadas por aplicaciones del sistema.

Por tanto, podemos concluir que las aplicaciones del sistema tienen las siguientes características:

  1. Puede llamar a las API privadas que no se divulgan en el SDK de Android.
  2. Tener mayores privilegios del sistema.
  3. Está integrado directamente en la ROM de Android y no se puede desinstalar mediante métodos ordinarios.

Requisitos previos de la aplicación del sistema

A continuación, demostramos cómo escribir una aplicación del sistema Android, pero antes de eso, debemos realizar los siguientes preparativos:

Paso 1, haz un paquete API

Las características de las aplicaciones del sistema determinan que su método de desarrollo no sea exactamente el mismo que el de las aplicaciones ordinarias de Android. En primer lugar, la aplicación del sistema puede llamar a la API oculta del SDK de Android, lo que requiere que introduzcamos el paquete jar que contiene la API oculta. Por supuesto, si no necesita llamar a la API oculta, puede omitir este paso. En el proyecto real, este paso será asistido por los colegas responsables del desarrollo del marco, ya que la capa farmework generalmente tiene nuevas interfaces que deben empaquetarse juntas.

1) Compilar el marco de Android

Podemos usar make frameworkel comando para compilar el código fuente del framework, o usarlo mmm frameworks/basey /framework/baseejecutarlo en el directorio mm.

Pero tenga en cuenta que el comando make va seguido del nombre del módulo en lugar de la ruta del módulo, por lo que no se puede escribir como marcos aquí.

Para compilar Android 11 y versiones posteriores, se han ajustado las instrucciones de compilación, utilicemake framework-minus-apex

Después de que la compilación sea exitosa, ingrese /out/target/common/obj/JAVA_LIBRARIES/framework-minus-apex_intermediates/al directorio, que contiene classes-header.jarel paquete jar que necesitamos.

classes-header.jarContiene API que no son públicas en el SDK de Android, por ejemplo: se usa para habilitar el mecanismo RRO OverlayManager.

Si no ha descargado el código fuente de AOSP, puede descargar el framework.jar compilado desde el repositorio de github de este video. La dirección de github [https://github.com/linxu-link/CarAndroidCourse] se puede ver en la introducción de este vídeo.

2) Importar a Android Studio

Después de generar framework.jar, lo importamos a Android Studio y agregamos el siguiente código a build.gradle en el directorio del proyecto.

allprojects{
    gradle.projectsEvaluated {
        //Arctic Fox
        tasks.withType(JavaCompile) {
            Set<File> fileSet = options.bootstrapClasspath.getFiles()
            List<File> newFileList = new ArrayList<>();
            newFileList.add(new File("./app/libs/framework_header.jar"))
            newFileList.addAll(fileSet)
            options.bootstrapClasspath = files(
                    newFileList.toArray()
            )
        }
    }
}

Introduzca el paquete jar en forma de compileOnly en build.gradle del directorio de la aplicación.

compileOnly files('libs/framework_header.jar')

Paso 2, hacer una firma del sistema

El sistema Android identificará el tipo de firma de la aplicación y otorgará a la aplicación el nivel de permiso correspondiente según el tipo de firma. Una condición importante para actualizar una aplicación común a una aplicación del sistema es que la aplicación necesita usar la firma del sistema. Entonces, en este paso, primero debemos hacer una firma del sistema, para que podamos depurar la aplicación durante el desarrollo.

1) La consola ingresa al directorio de compilación de AOSP

cd build/target/product/security

2) Hacer una firma del sistema

openssl pkcs8 -in platform.pk8 -inform DER -outform PEM -out [platform.pem]0 -nocrypt

openssl pkcs12 -export -in platform.x509.pem -inkey [platform.pem] -out [platform.pk12] -name [key的别名] -password pass:[key的密码]

keytool -importkeystore -deststorepass [key的密码] -destkeypass [key的密码] -destkeystore platform.jks -srckeystore platform.pk12 -srcstoretype PKCS12 -srcstorepass [key的密码] -alias [android]

Una vez completada la producción, se descargará y generará un archivo de firma plataforma.jks en el directorio actual, y se importará a Android Studio para firmar la aplicación.

3) Importar a Android Studio

Coloque platform.jks en el directorio de aplicaciones y agregue el siguiente código a build.gradle.

signingConfigs {
    sign {
        storeFile file('platform.jks')
        storePassword '123456'
        keyAlias 'android'
        keyPassword '123456'
    }
}

buildTypes {
    release {
        minifyEnabled false
        signingConfig signingConfigs.sign
    }
    debug {
        minifyEnabled false
        signingConfig signingConfigs.sign
    }
}

Después de introducir la firma del sistema en el estudio de Android, el proyecto de la aplicación puede llamar directamente a la API del sistema en el emulador de Android y, al mismo tiempo, obtener permisos de nivel superior.

Nota: El archivo de clave de prueba basado en el código fuente de AOSP generalmente no se puede usar en un entorno real (como un teléfono móvil), y el proyecto montado en un vehículo es más complicado. Algunos proyectos usarán una verificación de firma más estricta durante la etapa de desarrollo. por lo que los archivos de firma AOSP tampoco se pueden utilizar. Sin embargo, también hay proyectos que cambiarán la firma en la etapa final de producción en masa, por lo que la clave de prueba en AOSP aún se puede usar antes de esa fecha.

La información complementaria sobre el documento de firma es la siguiente:

En el directorio build/target/product/security/ del código fuente de Android, hay los siguientes 5 pares de CLAVES comunes:

  • media.pk8 da media.x509.pem

    Una clave de prueba para el paquete apk incluido con el sistema multimedia/descarga.

  • plataforma.pk8与plataforma.x509.pem

    Clave de prueba para el paquete apk incluido de la plataforma central.

  • shared.pk8与shared.x509.pem

    Clave de prueba para contenido compartido en el proceso Inicio/Contactos.

  • clave de prueba.pk8 y clave de prueba.x509.pem

    Clave predeterminada genérica para aplicaciones que no especifican una clave.

  • networkstack.pk8与networkstack.x509.pem

    Clave de prueba para paquetes apk contenidos en sistemas web.

Entre ellos, el archivo " .pk8" es la clave privada y el archivo ".x509.pem" es la clave pública. Tenga en cuenta que las claves de prueba en este directorio son solo para desarrollo y no deben usarse para firmar paquetes en imágenes publicadas públicamente.

Para obtener más información sobre las claves, puede leer la documentación oficial: https://source.android.com/docs/core/ota/sign_builds?hl=zh-cn

¿Y cómo se corresponden estas claves con el APK firmado? Android.bpHay un campo en el archivo en el directorio del código fuente del APK certificate, que se usa para especificar la CLAVE utilizada para firmar. Si no se especifica, se usa testkey de forma predeterminada. En correspondencia con la aplicación del sistema, certificatese pueden configurar los siguientes valores.

certificate: "platform"
certificate: "shared"
certificate: "media"

Para estas configuraciones en , debe agregar el siguiente contenido Android.bpal nodo en el archivo AndroidManifest.xml del código fuente del APK :<manifest>

android:sharedUserId="android.uid.system"
android:sharedUserId="android.uid.shared"
android:sharedUserId="android.media"

Aplicación del sistema de práctica

Paso 1, definir requisitos

Para permitirle sentir intuitivamente la diferencia entre "aplicaciones del sistema" y "aplicaciones comunes", requerimos que las "aplicaciones del sistema" completen las siguientes funciones:

  1. La aplicación se inicia automáticamente después de que se enciende el sistema, es decir, se inicia automáticamente cuando se enciende el sistema
  2. Superponga una Vista en la pantalla después de arrancar y no requiere autorización para "mostrar encima de otras aplicaciones"
  3. Levantarse automáticamente después de que se elimine la aplicación, es decir, el proceso se mantiene vivo

Estas funciones son requisitos que son difíciles de lograr en "aplicaciones normales". Demostremos cómo lograrlos en "aplicaciones de sistema".

Paso 2, modifica AndroidManifest.xml

El propio sistema Android ha proporcionado los mecanismos correspondientes para realizar las dos funciones de arranque y mantenimiento de procesos, solo necesitamos configurarlos en el archivo manifest.xml.

  • persistente

Establece si la solicitud debe permanecer en el estado de residencia. El valor predeterminado falsese establece para truehabilitar el modo residente, que solo se aplica a las aplicaciones del sistema.

Después de activar el modo residente, la aplicación completará el inicio antes de que se reproduzca la animación de inicio del sistema Android, y la aplicación permanecerá en segundo plano, incluso si se elimina, se iniciará de inmediato.

<application
    android:persistent="true">

Además, hay algunas propiedades que se pueden usar más comúnmente en las aplicaciones del sistema que se pueden configurar, y las presentaremos una por una.

  • android: ID de usuario compartido

Configure el intercambio de datos entre diferentes usuarios. De forma predeterminada, Android asigna a cada aplicación su ID de usuario único. Si dos o más aplicaciones establecen esta propiedad en el mismo valor, todas esas aplicaciones compartirán el mismo ID, siempre que tengan exactamente la misma firma. Las aplicaciones con el mismo ID de usuario pueden acceder a los datos de las demás y, si lo desean, ejecutarse en el mismo proceso.

Esta propiedad quedó obsoleta en el nivel de API 29. Tenga en cuenta que, dado que las aplicaciones existentes no pueden eliminar este valor, dichas aplicaciones deben agregar android:sharedUserMaxSdkVersion="32" para evitar usar la ID de usuario compartida en las instalaciones de nuevos usuarios.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    android:sharedUserId="android.uid.system"
    android:sharedUserMaxSdkVersion="32">
  • DirectBootAware

Modo de arranque directo. El modo de inicio directo apareció después de Android 7.0. Cuando el dispositivo se encendió normalmente pero no se desbloqueó, se dice que el dispositivo está en modo DirectBoot. De forma predeterminada, las aplicaciones no se inician en modo DirectBoot, ni siquiera las aplicaciones del sistema.

Si es necesario iniciar la aplicación en modo DirectBoot, puede establecer el atributo directBootAware en verdadero en el archivo manifest.xml.

<application android:directBootAware="true" >

Algunos casos de uso de aplicaciones comunes que requieren ejecutarse en modo de arranque directo incluyen:

  1. Aplicaciones que tienen notificaciones programadas, como aplicaciones de despertador;
  2. Aplicaciones que brindan notificaciones importantes para el usuario, como aplicaciones de SMS;
  3. Aplicaciones que brindan servicios de accesibilidad, como Talkback;
  4. Servicios clave del sistema, como CarService, etc.

Tenga en cuenta que para las aplicaciones, el espacio de almacenamiento se divide en los siguientes dos tipos

  1. Almacenamiento cifrado de credenciales, área de almacenamiento cifrado de credenciales. El lugar donde se almacenan los datos de forma predeterminada, solo está disponible después de que el usuario desbloquea el teléfono.
  2. Dispositivo de almacenamiento cifrado, área de almacenamiento cifrado del dispositivo. La correspondencia principal es el espacio de almacenamiento utilizado por DirectBoot. Este espacio de almacenamiento se puede utilizar tanto en modo DirectBoot como después de que el usuario desbloquee el teléfono.

0- Cuando el sistema Android está encendido, primero ingresa en modo DirectBoot. Si la aplicación necesita acceder a datos locales cuando se ejecuta en modo DirectBoot, se puede crear Context.createDeviceProtectedStorageContext()una instancia especial Contextllamando. Todas las llamadas API de clase de almacenamiento realizadas a través de esta instancia pueden acceder al almacenamiento cifrado del dispositivo. Como sigue:

    Context directBootContext = appContext.createDeviceProtectedStorageContext();
    // Access appDataFilename that lives in device encrypted storage
    FileInputStream inStream = directBootContext.openFileInput(appDataFilename);
    // Use inStream to read content...

Si necesita monitorear cuando la pantalla está desbloqueada, puede registrarse para la siguiente transmisión

    <receiver
      android:directBootAware="true" >
      ...
      <intent-filter>
        <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
      </intent-filter>
    </receiver>
    

Algunas aplicaciones o servicios críticos del sistema deben iniciarse y comenzar a ejecutarse antes de que se desbloquee la pantalla de Android. En este caso, se puede configurar como un modo de arranque directo. En este momento, debe leer detenidamente los documentos oficiales para evitar errores inesperados. Documentos oficiales: https://developer.android.google.cn/training/articles/direct-boot?hl=zh-cn

  • biblioteca de usos

Especifica la biblioteca compartida con la que se debe asociar la aplicación. Esta etiqueta le dice al sistema que agregue el código de la biblioteca al cargador de clases del paquete.

En el proyecto de la aplicación del vehículo, se puede usar para cargar algunas bibliotecas compartidas personalizadas del marco.

<uses-library
  android:name="string"
  android:required=["true" | "false"] />

android:nameEl nombre de la biblioteca. Este nombre viene dado por la documentación del paquete que utiliza. Por ejemplo, " android.test.runner" es un paquete que contiene clases de prueba de Android.

android:requiredIndica si la aplicación requiere la biblioteca android:nameespecificada :

  • "true": La aplicación no funcionará correctamente sin esta biblioteca. El sistema no permite la instalación de aplicaciones en dispositivos sin esta biblioteca.
  • "false": la aplicación puede usar esta biblioteca (si está presente), pero se ejecuta específicamente sin ella (si es necesario). El sistema permite instalar la aplicación incluso si esta biblioteca no existe. "false"Debe verificar esta biblioteca en tiempo de ejecución si usa

La configuración completa de androidmanifest.xml es la siguiente:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    android:sharedUserId="android.uid.system"
    android:sharedUserMaxSdkVersion="32"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

    <application
        android:name=".SystemApplication"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:persistent="true"
        android:supportsRtl="true"
        android:theme="@style/Theme.SystemApp">

        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".SystemService"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.android.systemapp.action" />
            </intent-filter>
        </service>
    </application>

</manifest>

Paso 3, escribir código lógico

Solo hay un Servicio en esta aplicación, inicie el Servicio en la Aplicación.

class SystemApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        Log.e("System", "System APP started")
        val intent = Intent()
        intent.setPackage("com.android.systemapp")
        intent.setAction("com.android.systemapp.action")
        startService(intent)
    }
}

En Servicio, dibujamos una Vista a través de WindowManager. Antes de que se reproduzca la animación del sistema, la Vista no se puede dibujar ni mostrar. En otras palabras, cuando se puede dibujar la vista, la animación del sistema ha terminado de reproducirse y se ha mostrado la interfaz de usuario del sistema.

// 创建用于 window 显示的context
val dm = getSystemService(DisplayManager::class.java)
val defaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY)
val defaultDisplayContext = createDisplayContext(defaultDisplay)
val ctx = defaultDisplayContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null);

// 在屏幕上绘制一个像素的view,用于监控开机动画是否播放完毕
val mWindowManager = ctx.getSystemService(WindowManager::class.java)
val bounds = mWindowManager.getCurrentWindowMetrics().getBounds();

val windowSizeTest: View = object : View(ctx) {
    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        Log.e(TAG, "system launch")
    }
}

El código completo del Servicio es el siguiente:

class SystemService : Service() {

    private val TAG = SystemService::class.java.simpleName;

    override fun onBind(intent: Intent): IBinder? {
        return null
    }

    override fun onCreate() {
        super.onCreate()
        // 创建用于 window 显示的context
        val dm = getSystemService(DisplayManager::class.java)
        val defaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY)
        val defaultDisplayContext = createDisplayContext(defaultDisplay)
        val ctx = defaultDisplayContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null);

        // 在屏幕上绘制一个像素的view,用于监控开机动画是否播放完毕
        val mWindowManager = ctx.getSystemService(WindowManager::class.java)
        val bounds = mWindowManager.getCurrentWindowMetrics().getBounds();

        val windowSizeTest: View = object : View(ctx) {
            override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
                // 暂停5秒后,移除该View
                Thread{
                    sleep(5_000)
                    mWindowManager.removeView(this)
                }.start()
            }
        }
        val testParams: WindowManager.LayoutParams = WindowManager.LayoutParams(
            WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    and WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                    and WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
        )
        testParams.width = bounds.width() / 2
        testParams.height = bounds.height()/2
        testParams.gravity = Gravity.CENTER
        testParams.title = TAG
        mWindowManager.addView(windowSizeTest, testParams)
    }
}

Paso 4, Verificar

En este paso, usamos el emulador en Android Studio para verificar que la aplicación del sistema se está ejecutando como esperamos.

Empuje la aplicación del sistema escrita a System/app/, pero debido a que la partición del sistema del emulador no está abierta para obtener permiso de escritura, primero debemos obtener el permiso de escritura de la partición del sistema.

1) Modificar el permiso de escritura del simulador

Primero ingresa al directorio del emulador de Android SDK y ejecuta el siguiente comando, y en la consola aparece el mensaje remontaje exitoso, lo que significa que la modificación del permiso de escritura es exitosa.

./emulator -list-avds
./emulator -writable-system -avd [10.1_WXGA_Tablet_API_31] -no-snapshot-load -qemu // 修改分区写入权限吧
adb root
adb remount
adb reboot // 重启模拟器
// 等待模拟器重启后
adb root
adb remount

2) Empuje la aplicación apk al directorio system/app/xxx

Cree uno nuevo (con cualquier nombre) en el directorio del sistema/aplicación SystemAppy luego envíe el apk a este directorio.

3) Reinicie el emulador para ver el efecto.

Después de que se reinicie el emulador, el proceso de SystemApp se iniciará automáticamente y cubrirá una vista negra en la pantalla Durante todo el proceso, SystemApp no ​​muestra una ventana de solicitud de permiso.

Si usamos adb kill [进程号]kill SystemApp, el sistema iniciará inmediatamente el proceso de SystemApp. El proceso de mantener vivo que es difícil de lograr en las aplicaciones ordinarias se puede lograr fácilmente en las "aplicaciones del sistema", y después de ingresar a la configuración del sistema para ver SystemApp, se encuentra que SystemApp en realidad no se puede desinstalar.

Resumir

En este video, presentamos el método de desarrollo de la aplicación del sistema Android. En el análisis final, el desarrollo de la aplicación Android del vehículo es el desarrollo de la aplicación del sistema. Comprender el método de desarrollo de la aplicación del sistema es el requisito técnico más básico para que podamos comenzar. con el desarrollo de la aplicación de Android para vehículos.

Bueno, eso es todo por este video. El contenido de texto de este video está publicado en mi cuenta pública personal de WeChat: "Car Android" y en mi blog personal. Los archivos PPT y el código fuente utilizados en el video están publicados en mi Github[https://github.com/linxu- link /CarAndroidCourse], puede encontrar la dirección correspondiente en la introducción de este video.

Gracias por mirar, nos vemos en el próximo video, adiós.

Referencias

https://developer.android.google.cn/guide/topics/manifest/application-element?hl=zh-cn#persistent

https://developer.android.google.cn/guide/topics/manifest/manifest-element?hl=zh-cn#uid

Supongo que te gusta

Origin blog.csdn.net/linkwj/article/details/129499289
Recomendado
Clasificación