From 8187065889d3c91c9e75f56cdd145ae82b820478 Mon Sep 17 00:00:00 2001 From: shafik Date: Thu, 5 Mar 2020 12:59:15 +0000 Subject: Add API for apps to query whether they have All Files Access This is a fix in response to developers' feedback on DP1. Add Environment API that enables apps to check whether they have All Files Access special app access. The API encapsulates the actual mechanics of the check, which are as follows: * First check MANAGE_EXTERNAL_STORAGE app-op, if it's allowed, return true. If it's denied (ignored or errored), return false. * If the mode is default, then check for MANAGE_EXTERNAL_STORAGE permission, if it's GRANTED (not just declared), then return true, else return false. Also add test. Test: atest EnvironmentTest # not CTS Fix: 150115615 Merged-In: I0574827c22960bf8f074313d983f289be7142149 Change-Id: I0574827c22960bf8f074313d983f289be7142149 --- api/current.txt | 3 +- core/java/android/os/Environment.java | 45 ++++++++++++++++++++++ .../coretests/src/android/os/EnvironmentTest.java | 41 +++++++++++++++++++- 3 files changed, 87 insertions(+), 2 deletions(-) diff --git a/api/current.txt b/api/current.txt index 1fe78d3edb52..89e0edefbdd2 100644 --- a/api/current.txt +++ b/api/current.txt @@ -36151,6 +36151,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; @@ -82224,4 +82226,3 @@ package org.xmlpull.v1.sax2 { } } - diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 21a1e0f0a108..f2fb5b246f39 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -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. + *

Declaring the permission {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} isn't + * enough to gain the access. + *

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} + *

Declaring the permission {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} isn't + * enough to gain the access. + *

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); diff --git a/core/tests/coretests/src/android/os/EnvironmentTest.java b/core/tests/coretests/src/android/os/EnvironmentTest.java index d98ceaf57dd9..c0325caf1425 100644 --- a/core/tests/coretests/src/android/os/EnvironmentTest.java +++ b/core/tests/coretests/src/android/os/EnvironmentTest.java @@ -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}. + * + *

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); + } + } } -- cgit v1.2.3-59-g8ed1b