summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Tyler Saunders <tylersaunders@google.com> 2023-01-30 12:47:07 +0000
committer Tyler Saunders <tylersaunders@google.com> 2023-01-31 14:18:19 +0000
commit78122a865e8b826c835a5c6ea677d7d900b5075a (patch)
tree1b642bcce4f7d3d2db7ace4877c05d215d285bac
parenta7bf9bc5891d29d278a0e6e68846fe4a52033b77 (diff)
Add shell support for creating media_grants.
* Allows inserting media_grant rows via an adb shell content call. * Renamed sqlite3.sh to mediaproviderutils.sh & included a new helper function to make adding grants simpler. * Add end-to-end test of MediaStore.grantMediaReadForPackage Sample adb command to add a media grant: ``` adb shell content call --method 'grant_media_read_for_package' \ --uri 'content://media' \ --extra 'uri':s:$URI \ --extra 'android.intent.extra.PACKAGE_NAME':s:$PACKAGE_NAME ``` Change-Id: I4ddfa43c5617af63f470e9c2f376e9c665b26d96
-rw-r--r--mediaproviderutils.sh (renamed from sqlite3.sh)66
-rw-r--r--src/com/android/providers/media/MediaProvider.java46
-rw-r--r--tests/src/com/android/providers/media/MediaProviderTest.java32
3 files changed, 133 insertions, 11 deletions
diff --git a/sqlite3.sh b/mediaproviderutils.sh
index cf9b4a58b..e3c5c08da 100644
--- a/sqlite3.sh
+++ b/mediaproviderutils.sh
@@ -1,5 +1,67 @@
+# Shell utility functions for mediaprovider developers.
# sudo apt-get install rlwrap to have a more fully featured sqlite CLI
-set -x
+set -x # enable debugging
+
+function add-media-grant () {
+# add a media grant to -p package for -id file_id
+
+ function usage() {
+
+ cat <<EOF
+
+ Usage: $(basename "$BASH_SOURCE[0]") [-i] id value [-p] package value
+
+ Adds a media grant for specified package and file._id
+
+ Available Options:
+
+ -i, --id The files._id in mediaprovider database
+ -p, --package Package name i.e. com.android.package
+
+EOF
+
+
+ }
+
+ # If we don't have any params, just print the documentation.
+ if [ -z "$1" ]
+ then
+
+ usage
+
+ else
+
+ # parse incoming arguments
+ while [[ "$#" -gt 0 ]]
+ do case $1 in
+ -i|--id) id="$2"
+ shift;;
+ -p|--package) packagename="$2"
+ shift;;
+ *) usage; return
+ esac
+ shift
+ done
+
+ echo "Adding media_grant for id=$id to package $packagename"
+
+ uri='content\\://media/picker/0/com.android.providers.media.photopicker/media/'
+ uriWithId="${uri}$id"
+
+ if [ -z "$id" ] || [ -z "$packagename" ]
+ then
+ usage; return
+ fi
+
+
+ adb wait-for-device
+ adb shell content call --method 'grant_media_read_for_package' \
+ --uri 'content://media' \
+ --extra 'uri':s:"$uriWithId" \
+ --extra 'android.intent.extra.PACKAGE_NAME':s:"$packagename"
+
+ fi
+}
function sqlite3-pull () {
adb root
@@ -87,3 +149,5 @@ function get-package() {
echo "com.android.providers.media.module"
fi
}
+
+set +x # disable debugging
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index e5f36edcd..5541de3b8 100644
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -145,6 +145,7 @@ import static com.android.providers.media.util.FileUtils.toFuseFile;
import static com.android.providers.media.util.Logging.LOGV;
import static com.android.providers.media.util.Logging.TAG;
import static com.android.providers.media.util.PermissionUtils.checkPermissionSelf;
+import static com.android.providers.media.util.PermissionUtils.checkPermissionShell;
import static com.android.providers.media.util.StringUtils.componentStateToString;
import static com.android.providers.media.util.SyntheticPathUtils.REDACTED_URI_ID_PREFIX;
import static com.android.providers.media.util.SyntheticPathUtils.REDACTED_URI_ID_SIZE;
@@ -6366,18 +6367,43 @@ public class MediaProvider extends ContentProvider {
}
}
case MediaStore.GRANT_MEDIA_READ_FOR_PACKAGE_CALL: {
- if (!checkPermissionSelf(Binder.getCallingUid())) {
+ final int caller = Binder.getCallingUid();
+ final List<Uri> uris;
+ final String packageName;
+ if (checkPermissionSelf(caller)) {
+ // If the caller is MediaProvider the accepted parameters are EXTRA_URI_LIST
+ // and EXTRA_UID.
+ if (!extras.containsKey(
+ MediaStore.EXTRA_URI_LIST)
+ && !extras.containsKey(Intent.EXTRA_UID)) {
+ throw new IllegalArgumentException(
+ "Missing required extras arguments: EXTRA_URI_LIST or"
+ + " EXTRA_UID");
+ }
+ uris = extras.getParcelableArrayList(MediaStore.EXTRA_URI_LIST);
+ final PackageManager pm = getContext().getPackageManager();
+ final int packageUid = extras.getInt(Intent.EXTRA_UID);
+ packageName = pm.getNameForUid(packageUid);
+ } else if (checkPermissionShell(caller)) {
+ // If the caller is the shell, the accepted parameters are EXTRA_URI (as string)
+ // and EXTRA_PACKAGE_NAME (as string).
+ if (!extras.containsKey(MediaStore.EXTRA_URI)
+ && !extras.containsKey(Intent.EXTRA_PACKAGE_NAME)) {
+ throw new IllegalArgumentException(
+ "Missing required extras arguments: EXTRA_URI or"
+ + " EXTRA_PACKAGE_NAME");
+ }
+ packageName = extras.getString(Intent.EXTRA_PACKAGE_NAME);
+ uris = List.of(Uri.parse(extras.getString(MediaStore.EXTRA_URI)));
+ } else {
+ // All other callers are unauthorized.
throw new SecurityException("Create media grants not allowed. "
- + " Calling app ID:" + UserHandle.getAppId(Binder.getCallingUid())
- + " Calling UID:" + Binder.getCallingUid()
- + " Media Provider app ID:" + UserHandle.getAppId(MY_UID)
- + " Media Provider UID:" + MY_UID);
+ + " Calling app ID:" + UserHandle.getAppId(Binder.getCallingUid())
+ + " Calling UID:" + Binder.getCallingUid()
+ + " Media Provider app ID:" + UserHandle.getAppId(MY_UID)
+ + " Media Provider UID:" + MY_UID);
}
- final int packageUid = extras.getInt(Intent.EXTRA_UID);
- final ArrayList<Uri> uris =
- extras.getParcelableArrayList(MediaStore.EXTRA_URI_LIST);
- final PackageManager pm = getContext().getPackageManager();
- final String packageName = pm.getNameForUid(packageUid);
+
mMediaGrants.addMediaGrantsForPackage(packageName, uris);
return null;
}
diff --git a/tests/src/com/android/providers/media/MediaProviderTest.java b/tests/src/com/android/providers/media/MediaProviderTest.java
index 7dbca1b83..5d83c9fd1 100644
--- a/tests/src/com/android/providers/media/MediaProviderTest.java
+++ b/tests/src/com/android/providers/media/MediaProviderTest.java
@@ -72,6 +72,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.providers.media.MediaProvider.FallbackException;
import com.android.providers.media.MediaProvider.VolumeArgumentException;
import com.android.providers.media.MediaProvider.VolumeNotFoundException;
+import com.android.providers.media.photopicker.PickerSyncController;
import com.android.providers.media.util.FileUtils;
import com.android.providers.media.util.FileUtilsTest;
import com.android.providers.media.util.SQLiteQueryBuilder;
@@ -306,6 +307,37 @@ public class MediaProviderTest {
assertNotNull(MediaStore.createWriteRequest(sIsolatedResolver, uris));
}
+ @Test
+ public void testGrantMediaReadForPackage() throws Exception {
+ final File dir = Environment
+ .getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
+ final File testFile = stage(R.raw.lg_g4_iso_800_jpg,
+ new File(dir, "test" + System.nanoTime() + ".jpg"));
+ final Uri uri = MediaStore.scanFile(sIsolatedResolver, testFile);
+ Long fileId = ContentUris.parseId(uri);
+
+ final Uri.Builder builder = Uri.EMPTY.buildUpon();
+ builder.scheme("content");
+ builder.encodedAuthority(MediaStore.AUTHORITY);
+
+ final Uri testUri = builder.appendPath("picker")
+ .appendPath(Integer.toString(UserHandle.myUserId()))
+ .appendPath(PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY)
+ .appendPath(MediaStore.AUTHORITY)
+ .appendPath(Long.toString(fileId))
+ .build();
+
+ try {
+ MediaStore.grantMediaReadForPackage(sIsolatedContext,
+ android.os.Process.myUid(),
+ List.of(testUri));
+ } finally {
+ dir.delete();
+ testFile.delete();
+ }
+
+ }
+
/**
* We already have solid coverage of this logic in
* {@code CtsProviderTestCases}, but the coverage system currently doesn't