Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
test(android): accept granted documents from multiple authorities
  • Loading branch information
jabrailkhalil committed Oct 1, 2026
commit 250ebeffa44d5c266013150f41b5db7829360e00
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<application>
<provider
android:name="com.qwen.mobileshell.FilePickerFixtureProvider"
android:authorities="com.qwen.mobileshell.test.picker"
android:authorities="com.qwen.mobileshell.test.picker;com.qwen.mobileshell.test.picker.second"
android:exported="true"
android:grantUriPermissions="true"
android:readPermission="com.qwen.mobileshell.test.READ_PICKER"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.content.ContextWrapper
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.os.Process
import android.webkit.ValueCallback
import android.webkit.WebChromeClient.FileChooserParams
Expand All @@ -28,11 +29,11 @@ class FilePickerDeviceTest {
if (fixtureUsed) context.contentResolver.call(FilePickerFixtureProvider.BASE_URI, "reset", null, null)
}

private fun document(index: Int = 0, granted: Boolean = true): Uri {
private fun document(index: Int = 0, granted: Boolean = true, base: Uri = FilePickerFixtureProvider.BASE_URI): Uri {
if (!fixtureUsed) context.contentResolver.call(FilePickerFixtureProvider.BASE_URI, "reset", null, null)
fixtureUsed = true
val uri = FilePickerFixtureProvider.uri(index)
if (granted) context.contentResolver.call(FilePickerFixtureProvider.BASE_URI, "grant", index.toString(), null)
val uri = FilePickerFixtureProvider.uri(base, index)
if (granted) context.contentResolver.call(base, "grant", index.toString(), Bundle().apply { putString("authority", base.authority) })
val provider = requireNotNull(context.packageManager.resolveContentProvider(uri.authority!!, 0))
assertNotEquals("Fixture must be owned by a different UID", context.applicationInfo.uid, provider.applicationInfo.uid)
assertEquals("Fixture URI grant", if (granted) PackageManager.PERMISSION_GRANTED else PackageManager.PERMISSION_DENIED,
Expand Down Expand Up @@ -194,6 +195,30 @@ class FilePickerDeviceTest {
assertEquals(2, countingContext.providerManagerReads)
}

@Test fun multipleGrantedAuthoritiesPreserveOrderAndResolveEachProviderOnce() {
val uris = listOf(
document(0), document(0, base = FilePickerFixtureProvider.SECOND_BASE_URI),
document(1), document(1, base = FilePickerFixtureProvider.SECOND_BASE_URI),
)
val countingContext = CountingContext(context)
val actual = delivered(selection(uris), pickerContext = countingContext)
assertArrayEquals(uris.toTypedArray(), actual)
assertEquals(2, countingContext.providerManagerReads)
assertEquals(uris.size, countingContext.permissionChecks)
actual!!.forEach { uri ->
val bytes = context.contentResolver.openInputStream(uri)!!.use { it.readBytes() }
assertArrayEquals(FilePickerFixtureProvider.contents(uri.lastPathSegment!!.toInt()), bytes)
}
}

@Test fun mixedAuthoritiesStillRequireSeparateGrantsForTheSameDocumentIndex() {
val uris = listOf(document(0), document(0, granted = false, base = FilePickerFixtureProvider.SECOND_BASE_URI))
val countingContext = CountingContext(context)
assertNull(delivered(selection(uris), pickerContext = countingContext))
assertEquals(2, countingContext.providerManagerReads)
assertEquals(2, countingContext.permissionChecks)
}

@Test fun unsupportedModeAndStaleDocumentCancelWithoutLaunching() {
val calls = mutableListOf<Array<Uri>?>()
val picker = NativeFilePicker(context) { fail("Must not launch") }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
public class FilePickerFixtureProvider extends ContentProvider {
private static final String TARGET = "com.qwen.mobileshell";
public static final Uri BASE_URI = Uri.parse("content://com.qwen.mobileshell.test.picker");
public static final Uri SECOND_BASE_URI = Uri.parse("content://com.qwen.mobileshell.test.picker.second");

@Override public boolean onCreate() { return true; }

Expand All @@ -36,10 +37,13 @@ public class FilePickerFixtureProvider extends ContentProvider {
long identity = Binder.clearCallingIdentity();
try {
if ("grant".equals(method)) {
owner.grantUriPermission(TARGET, uri(Integer.parseInt(arg)), Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri base = extras == null ? BASE_URI : Uri.parse("content://" + extras.getString("authority"));
owner.grantUriPermission(TARGET, uri(base, Integer.parseInt(arg)), Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else if ("reset".equals(method)) {
for (int index = 0; index <= 100; index++) {
owner.revokeUriPermission(TARGET, uri(index), Intent.FLAG_GRANT_READ_URI_PERMISSION);
for (Uri base : new Uri[] {BASE_URI, SECOND_BASE_URI}) {
owner.revokeUriPermission(TARGET, uri(base, index), Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
File file = new File(owner.getCacheDir(), "picker-fixture-" + index + ".txt");
if (file.exists() && !file.delete()) throw new IllegalStateException("Cannot remove fixture file");
}
Expand All @@ -55,7 +59,8 @@ public class FilePickerFixtureProvider extends ContentProvider {
@Override public ParcelFileDescriptor openFile(Uri selected, String mode) throws FileNotFoundException {
if (!"r".equals(mode)) throw new IllegalArgumentException("Fixture is read-only");
int index = Integer.parseInt(selected.getLastPathSegment());
if (!selected.equals(uri(index))) throw new IllegalArgumentException("Unknown fixture URI");
Uri base = selected.buildUpon().path(null).build();
if (!selected.equals(uri(base, index))) throw new IllegalArgumentException("Unknown fixture URI");
Context owner = getContext();
if (owner == null) throw new IllegalStateException("Fixture provider is not attached");
File file = new File(owner.getCacheDir(), "picker-fixture-" + index + ".txt");
Expand All @@ -75,9 +80,10 @@ public class FilePickerFixtureProvider extends ContentProvider {
@Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { throw new UnsupportedOperationException(); }
@Override public int delete(Uri uri, String selection, String[] selectionArgs) { throw new UnsupportedOperationException(); }

public static Uri uri(int index) {
public static Uri uri(Uri base, int index) {
if (!BASE_URI.equals(base) && !SECOND_BASE_URI.equals(base)) throw new IllegalArgumentException("Unknown fixture authority");
if (index < 0 || index > 100) throw new IllegalArgumentException("Unknown fixture index");
return BASE_URI.buildUpon().appendPath(Integer.toString(index)).build();
return base.buildUpon().appendPath(Integer.toString(index)).build();
}

public static byte[] contents(int index) {
Expand Down
Loading