Mise en cache de l'APK

Ce document décrit la conception d'une solution de mise en cache d'APK pour une installation rapide des applications préchargées sur un appareil compatible avec les partitions A/B.

Les OEM peuvent placer des préchargements et des applications populaires dans le cache APK stocké dans la partition B, qui est presque vide, sur les nouveaux appareils partitionnés en A/B sans affecter l'espace de données visible par l'utilisateur. En disposant d'un cache APK sur l'appareil, les appareils neufs ou récemment réinitialisés sont prêts à l'emploi presque immédiatement, sans avoir à télécharger de fichiers APK depuis Google Play.

Cas d'utilisation

  • Stocker les applications préchargées dans la partition B pour une configuration plus rapide
  • Stocker les applications populaires dans la partition B pour une restauration plus rapide

Prérequis

Pour utiliser cette fonctionnalité, l'appareil doit:

  • Version Android 8.1 (O MR1) installée
  • Partition A/B implémentée

Le contenu préchargé ne peut être copié que lors du premier démarrage. En effet, sur les appareils compatibles avec les mises à jour système A/B, la partition B ne stocke pas réellement de fichiers d'image système, mais du contenu préchargé tel que des ressources de démonstration en magasin, des fichiers OAT et le cache APK. Une fois les ressources copiées dans la partition /data (cela se produit au premier démarrage), la partition B est utilisée par les mises à jour Over-The-Air (OTA) pour télécharger les versions mises à jour de l'image système.

Par conséquent, le cache APK ne peut pas être mis à jour via OTA. Il ne peut être préchargé qu'en usine. La réinitialisation d'usine n'affecte que la partition /data. La partition B du système contient toujours le contenu préchargé jusqu'à ce que l'image OTA soit téléchargée. Une fois la configuration d'usine rétablie, le système effectuera de nouveau son premier démarrage. Cela signifie que la mise en cache de l'APK n'est pas disponible si l'image OTA est téléchargée sur la partition B, puis que la configuration d'usine de l'appareil est rétablie.

Implémentation

Approche 1. Contenu de la partition system_other

Conseil: Le contenu préchargé n'est pas perdu après la réinitialisation d'usine. Il est copié à partir de la partition B après un redémarrage.

Inconvénient: nécessite de l'espace sur la partition B. Le démarrage après le rétablissement de la configuration d'usine nécessite un temps supplémentaire pour copier le contenu préchargé.

Pour que les préchargements soient copiés lors du premier démarrage, le système appelle un script dans /system/bin/preloads_copy.sh. Le script est appelé avec un seul argument (chemin d'accès au point d'installation en lecture seule de la partition system_b):

Pour implémenter cette fonctionnalité, apportez les modifications suivantes, spécifiques à l'appareil. Voici un exemple de Marlin:

  1. Ajoutez le script qui effectue la copie au fichier device-common.mk (dans ce cas, device/google/marlin/device-common.mk), comme suit:
    # Script that copies preloads directory from system_other to data partition
    PRODUCT_COPY_FILES += \
        device/google/marlin/preloads_copy.sh:system/bin/preloads_copy.sh
    
    Vous trouverez un exemple de source de script à l'adresse: device/google/marlin/preloads_copy.sh.
  2. Modifiez le fichier init.common.rc pour qu'il crée le répertoire et les sous-répertoires /data/preloads nécessaires:
    mkdir /data/preloads 0775 system system
    mkdir /data/preloads/media 0775 system system
    mkdir /data/preloads/demo 0775 system system
    
    Vous trouverez un exemple de source de fichier init à l'adresse: device/google/marlin/init.common.rc.
  3. Définissez un nouveau domaine SELinux dans le fichier preloads_copy.te:
    type preloads_copy, domain, coredomain;
    type preloads_copy_exec, exec_type, vendor_file_type, file_type;
    
    init_daemon_domain(preloads_copy)
    
    allow preloads_copy shell_exec:file rx_file_perms;
    allow preloads_copy toolbox_exec:file rx_file_perms;
    allow preloads_copy preloads_data_file:dir create_dir_perms;
    allow preloads_copy preloads_data_file:file create_file_perms;
    allow preloads_copy preloads_media_file:dir create_dir_perms;
    allow preloads_copy preloads_media_file:file create_file_perms;
    
    # Allow to copy from /postinstall
    allow preloads_copy system_file:dir r_dir_perms;
    
    Vous trouverez un exemple de fichier de domaine SELinux à l'adresse: /device/google/marlin/+/main/sepolicy/preloads_copy.te.
  4. Enregistrez le domaine dans un nouveau fichier /sepolicy/file_contexts:
    /system/bin/preloads_copy\.sh     u:object_r:preloads_copy_exec:s0
    
    Vous trouverez un exemple de fichier de contextes SELinux à l'adresse: device/google/marlin/sepolicy/preloads_copy.te.
  5. Au moment de la compilation, le répertoire contenant le contenu préchargé doit être copié sur la partition system_other:
    # Copy contents of preloads directory to system_other partition
    PRODUCT_COPY_FILES += \
        $(call find-copy-subdir-files,*,vendor/google_devices/marlin/preloads,system_other/preloads)
    
    Il s'agit d'un exemple de modification dans un Makefile qui permet de copier les ressources de cache APK à partir du dépôt Git du fournisseur (dans notre cas, il s'agissait de vendor/google_devices/marlin/preloads) à l'emplacement de la partition system_other qui sera ensuite copiée dans /data/preloads lorsque l'appareil démarre pour la première fois. Ce script s'exécute au moment de la compilation pour préparer l'image system_other. Il s'attend à ce que le contenu préchargé soit disponible dans vendor/google_devices/marlin/preloads. L'OEM est libre de choisir le nom/chemin d'accès du dépôt réel.
  6. Le cache APK se trouve dans /data/preloads/file_cache et se présente comme suit:
    /data/preloads/file_cache/
        app.package.name.1/
              file1
              fileN
        app.package.name.N/
    
    Il s'agit de la structure de répertoire finale sur les appareils. Les OEM sont libres de choisir n'importe quelle approche d'implémentation, à condition que la structure de fichiers finale reproduise celle décrite ci-dessus.

Approche 2. Contenu de l'image des données utilisateur flashé en usine

Cette autre approche suppose que le contenu préchargé est déjà inclus dans le répertoire /data/preloads de la partition /data.

Avantages: fonctionne dès la sortie de la boîte. Vous n'avez pas besoin de personnaliser l'appareil pour copier des fichiers au premier démarrage. Le contenu se trouve déjà sur la partition /data.

Inconvénient: le contenu préchargé est perdu après une réinitialisation d'usine. Bien que cela puisse être acceptable pour certains, cela ne fonctionne pas toujours pour les OEM qui réinitialisent les appareils en usine après avoir effectué des inspections de contrôle qualité.

Une nouvelle méthode @SystemApi, getPreloadsFileCache(), a été ajoutée à android.content.Context. Il renvoie un chemin d'accès absolu vers un répertoire spécifique à l'application dans le cache préchargé.

Une nouvelle méthode, IPackageManager.deletePreloadsFileCache, a été ajoutée pour supprimer le répertoire de préchargement afin de récupérer tout l'espace. La méthode ne peut être appelée que par les applications avec SYSTEM_UID, c'est-à-dire le serveur système ou les paramètres.

Préparation de l'application

Seules les applications privilégiées peuvent accéder au répertoire de cache de préchargement. Pour cet accès, les applications doivent être installées dans le répertoire /system/priv-app.

Validation

  • Après le premier démarrage, l'appareil doit contenir du contenu dans le répertoire /data/preloads/file_cache.
  • Le contenu du répertoire file_cache/ doit être supprimé si l'espace de stockage de l'appareil est insuffisant.

Utilisez l'exemple d'application ApkCacheTest pour tester le cache APK.

  1. Créez l'application en exécutant la commande suivante à partir du répertoire racine:
    make ApkCacheTest
    
  2. Installez l'application en tant qu'application privilégiée. (N'oubliez pas que seules les applications privilégiées peuvent accéder au cache APK.) Pour ce faire, vous devez disposer d'un appareil en mode root:
    adb root && adb remount
    adb shell mkdir /system/priv-app/ApkCacheTest
    adb push $ANDROID_PRODUCT_OUT/data/app/ApkCacheTest/ApkCacheTest.apk /system/priv-app/ApkCacheTest/
    adb shell stop && adb shell start
    
  3. Simulez le répertoire de cache de fichiers et son contenu si nécessaire (ce qui nécessite également des droits d'accès root):
    adb shell mkdir -p /data/preloads/file_cache/com.android.apkcachetest
    adb shell restorecon -r /data/preloads
    adb shell "echo "Test File" > /data/preloads/file_cache/com.android.apkcachetest/test.txt"
    
  4. Testez l'application. Après avoir installé l'application et créé le répertoire de test file_cache, ouvrez l'application ApkCacheTest. Un fichier test.txt et son contenu devraient s'afficher. Consultez cette capture d'écran pour voir comment ces résultats apparaissent dans l'interface utilisateur.

    Figure 1 : Résultats d'ApkCacheTest.