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

Commit f46b9c96 authored by Zimuzo Ezeozue's avatar Zimuzo Ezeozue Committed by Android (Google) Code Review
Browse files

Merge "Add API for apps to query whether they have All Files Access" into rvc-dev

parents 5463a87f 81870658
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -36154,6 +36154,8 @@ package android.os {
    method public static boolean isExternalStorageEmulated(@NonNull java.io.File);
    method public static boolean isExternalStorageLegacy();
    method public static boolean isExternalStorageLegacy(@NonNull java.io.File);
    method public static boolean isExternalStorageManager();
    method public static boolean isExternalStorageManager(@NonNull java.io.File);
    method public static boolean isExternalStorageRemovable();
    method public static boolean isExternalStorageRemovable(@NonNull java.io.File);
    field public static String DIRECTORY_ALARMS;
@@ -82232,4 +82234,3 @@ package org.xmlpull.v1.sax2 {
  }
}
+45 −0
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;

/**
 * Provides access to environment variables.
@@ -1253,6 +1254,50 @@ public class Environment {
                uid, context.getOpPackageName()) == AppOpsManager.MODE_ALLOWED;
    }

    /**
     * Returns whether the calling app has All Files Access on the primary shared/external storage
     * media.
     * <p>Declaring the permission {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} isn't
     * enough to gain the access.
     * <p>To request access, use
     * {@link android.provider.Settings#ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION}.
     */
    public static boolean isExternalStorageManager() {
        final File externalDir = sCurrentUser.getExternalDirs()[0];
        return isExternalStorageManager(externalDir);
    }

    /**
     * Returns whether the calling app has All Files Access at the given {@code path}
     * <p>Declaring the permission {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} isn't
     * enough to gain the access.
     * <p>To request access, use
     * {@link android.provider.Settings#ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION}.
     */
    public static boolean isExternalStorageManager(@NonNull File path) {
        final Context context = Objects.requireNonNull(AppGlobals.getInitialApplication());
        String packageName = Objects.requireNonNull(context.getPackageName());
        int uid = context.getApplicationInfo().uid;

        final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
        final int opMode =
                appOps.checkOpNoThrow(AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE, uid, packageName);

        switch (opMode) {
            case AppOpsManager.MODE_DEFAULT:
                return PackageManager.PERMISSION_GRANTED
                        == context.checkPermission(
                                Manifest.permission.MANAGE_EXTERNAL_STORAGE, Process.myPid(), uid);
            case AppOpsManager.MODE_ALLOWED:
                return true;
            case AppOpsManager.MODE_ERRORED:
            case AppOpsManager.MODE_IGNORED:
                return false;
            default:
                throw new IllegalStateException("Unknown AppOpsManager mode " + opMode);
        }
    }

    static File getDirectory(String variableName, String defaultPath) {
        String path = System.getenv(variableName);
        return path == null ? new File(defaultPath) : new File(path);
+40 −1
Original line number Diff line number Diff line
@@ -23,7 +23,10 @@ import static android.os.Environment.HAS_OTHER;
import static android.os.Environment.classifyExternalStorageDirectory;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import android.app.AppOpsManager;
import android.content.Context;

import androidx.test.InstrumentationRegistry;
@@ -40,10 +43,33 @@ import java.io.File;
public class EnvironmentTest {
    private File dir;

    private Context getContext() {
    private static Context getContext() {
        return InstrumentationRegistry.getContext();
    }

    /**
     * Sets {@code mode} for the given {@code ops} and the given {@code uid}.
     *
     * <p>This method drops shell permission identity.
     */
    private static void setAppOpsModeForUid(int uid, int mode, String... ops) {
        if (ops == null) {
            return;
        }
        InstrumentationRegistry.getInstrumentation()
                .getUiAutomation()
                .adoptShellPermissionIdentity();
        try {
            for (String op : ops) {
                getContext().getSystemService(AppOpsManager.class).setUidMode(op, uid, mode);
            }
        } finally {
            InstrumentationRegistry.getInstrumentation()
                    .getUiAutomation()
                    .dropShellPermissionIdentity();
        }
    }

    @Before
    public void setUp() throws Exception {
        dir = getContext().getDir("testing", Context.MODE_PRIVATE);
@@ -101,4 +127,17 @@ public class EnvironmentTest {
        Environment.buildPath(dir, "Taxes.pdf").createNewFile();
        assertEquals(HAS_OTHER, classifyExternalStorageDirectory(dir));
    }

    @Test
    public void testIsExternalStorageManager() throws Exception {
        assertFalse(Environment.isExternalStorageManager());
        try {
            setAppOpsModeForUid(Process.myUid(), AppOpsManager.MODE_ALLOWED,
                    AppOpsManager.OPSTR_MANAGE_EXTERNAL_STORAGE);
            assertTrue(Environment.isExternalStorageManager());
        } finally {
            setAppOpsModeForUid(Process.myUid(), AppOpsManager.MODE_DEFAULT,
                    AppOpsManager.OPSTR_MANAGE_EXTERNAL_STORAGE);
        }
    }
}