diff options
author | 2023-01-30 12:47:07 +0000 | |
---|---|---|
committer | 2023-01-31 14:18:19 +0000 | |
commit | 78122a865e8b826c835a5c6ea677d7d900b5075a (patch) | |
tree | 1b642bcce4f7d3d2db7ace4877c05d215d285bac | |
parent | a7bf9bc5891d29d278a0e6e68846fe4a52033b77 (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.java | 46 | ||||
-rw-r--r-- | tests/src/com/android/providers/media/MediaProviderTest.java | 32 |
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 |