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

Commit b74a224d authored by Automerger Merge Worker's avatar Automerger Merge Worker
Browse files

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

Change-Id: I36c2406239737a803337bd7741eeb9777e4c522f
parents f618d7d0 f46b9c96
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;
@@ -82233,4 +82235,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);
        }
    }
}