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

Commit cae40019 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: I09871dfc36bc3548cf9d43eb90d33fae7bc83427
parents 09003ca4 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;
@@ -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);
        }
    }
}