Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eff2ae20 authored by Winson Chiu's avatar Winson Chiu
Browse files

Expose Environment device/credential encrypted app storage directories

ApplicationInfo is expensive to build, especially when using the
new PackageState APIs which don't require ApplicationInfo at all.

To allow access to the dataDir field without ApplicationInfo, this
exposes methods on Environment to build them dynamically based on
volume UUID, user, and app package name. This avoids having to
build/cache the strings in system server and consolidates the
file system related APIs into Environment as a single source of
truth.

This also fixes the @TestApi StorageManager#convert so that its
String parameter is marked @Nullable, which works because
UUID_PRIVATE_INTERNAL is defined as nullable. This allows the method
to be used in Kotlin, as the Kotlin compiler will enforce nullability
annotations, preventing the UUID_PRIVATE_INTERNAL value from being
tested without the right annotation on the parameter.

Bug: 245943991

Test: atest android.os.EnvironmentTest
Test: atest android.os.cts.EnvironmentTest

Change-Id: I5983ee22a27f566352fd0de03f0ee421a0bc390f
parent e36b1ca4
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -9626,6 +9626,8 @@ package android.os {
  }
  public class Environment {
    method @NonNull public static java.io.File getDataCePackageDirectoryForUser(@NonNull java.util.UUID, @NonNull android.os.UserHandle, @NonNull String);
    method @NonNull public static java.io.File getDataDePackageDirectoryForUser(@NonNull java.util.UUID, @NonNull android.os.UserHandle, @NonNull String);
    method @NonNull public static java.util.Collection<java.io.File> getInternalMediaDirectories();
    method @NonNull public static java.io.File getOdmDirectory();
    method @NonNull public static java.io.File getOemDirectory();
+1 −1
Original line number Diff line number Diff line
@@ -2047,7 +2047,7 @@ package android.os.storage {

  public class StorageManager {
    method public long computeStorageCacheBytes(@NonNull java.io.File);
    method @NonNull public static java.util.UUID convert(@NonNull String);
    method @NonNull public static java.util.UUID convert(@Nullable String);
    method @NonNull public static String convert(@NonNull java.util.UUID);
    method @Nullable public String getCloudMediaProvider();
    method public boolean isAppIoBlocked(@NonNull java.util.UUID, int, int, int);
+56 −4
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.os;

import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -44,6 +45,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

/**
 * Provides access to environment variables.
@@ -551,12 +553,37 @@ public class Environment {
    }

    /** {@hide} */
    public static File getDataUserCePackageDirectory(String volumeUuid, int userId,
            String packageName) {
    @NonNull
    public static File getDataUserCePackageDirectory(@Nullable String volumeUuid, int userId,
            @NonNull String packageName) {
        // TODO: keep consistent with installd
        return new File(getDataUserCeDirectory(volumeUuid, userId), packageName);
    }

    /**
     * Retrieve the credential encrypted data directory for a specific package of a specific user.
     * This is equivalent to {@link ApplicationInfo#credentialProtectedDataDir}, exposed because
     * fetching a full {@link ApplicationInfo} instance may be expensive if all the caller needs
     * is this directory.
     *
     * @param storageUuid The storage volume for this directory, usually retrieved from a
     * {@link StorageManager} API or {@link ApplicationInfo#storageUuid}.
     * @param user The user this directory is for.
     * @param packageName The app this directory is for.
     *
     * @see ApplicationInfo#credentialProtectedDataDir
     * @return A file to the directory.
     *
     * @hide
     */
    @SystemApi
    @NonNull
    public static File getDataCePackageDirectoryForUser(@NonNull UUID storageUuid,
            @NonNull UserHandle user, @NonNull String packageName) {
        var volumeUuid = StorageManager.convert(storageUuid);
        return getDataUserCePackageDirectory(volumeUuid, user.getIdentifier(), packageName);
    }

    /** {@hide} */
    public static File getDataUserDeDirectory(String volumeUuid) {
        return new File(getDataDirectory(volumeUuid), DIR_USER_DE);
@@ -568,12 +595,37 @@ public class Environment {
    }

    /** {@hide} */
    public static File getDataUserDePackageDirectory(String volumeUuid, int userId,
            String packageName) {
    @NonNull
    public static File getDataUserDePackageDirectory(@Nullable String volumeUuid, int userId,
            @NonNull String packageName) {
        // TODO: keep consistent with installd
        return new File(getDataUserDeDirectory(volumeUuid, userId), packageName);
    }

    /**
     * Retrieve the device encrypted data directory for a specific package of a specific user. This
     * is equivalent to {@link ApplicationInfo#deviceProtectedDataDir}, exposed because fetching a
     * full {@link ApplicationInfo} instance may be expensive if all the caller needs is this
     * directory.
     *
     * @param storageUuid The storage volume for this directory, usually retrieved from a
     * {@link StorageManager} API or {@link ApplicationInfo#storageUuid}.
     * @param user The user this directory is for.
     * @param packageName The app this directory is for.
     *
     * @see ApplicationInfo#deviceProtectedDataDir
     * @return A file to the directory.
     *
     * @hide
     */
    @SystemApi
    @NonNull
    public static File getDataDePackageDirectoryForUser(@NonNull UUID storageUuid,
            @NonNull UserHandle user, @NonNull String packageName) {
        var volumeUuid = StorageManager.convert(storageUuid);
        return getDataUserDePackageDirectory(volumeUuid, user.getIdentifier(), packageName);
    }

    /**
     * Return preloads directory.
     * <p>This directory may contain pre-loaded content such as
+9 −0
Original line number Diff line number Diff line
@@ -84,6 +84,15 @@
          "include-filter": "android.os.cts.SharedMemoryTest"
        }
      ]
    },
    {
      "file_patterns": ["Environment[^/]*\\.java"],
      "name": "FrameworksCoreTests",
      "options": [
        {
          "include-filter": "android.os.EnvironmentTest"
        }
      ]
    }
  ],
  "postsubmit": [
+2 −1
Original line number Diff line number Diff line
@@ -2742,7 +2742,8 @@ public class StorageManager {

    /** {@hide} */
    @TestApi
    public static @NonNull UUID convert(@NonNull String uuid) {
    public static @NonNull UUID convert(@Nullable String uuid) {
        // UUID_PRIVATE_INTERNAL is null, so this accepts nullable input
        if (Objects.equals(uuid, UUID_PRIVATE_INTERNAL)) {
            return UUID_DEFAULT;
        } else if (Objects.equals(uuid, UUID_PRIMARY_PHYSICAL)) {
Loading