Query only necessary columns in ChooserActivity.extractFileInfo().

ChooserActivity started querying the content URI with a null projection
since Q, which means a default projection that includes all columns.
However, ChooserActivity.extractFileInfo() actually only needs the file
name and flags, so querying all columns usually means reading
unnecessary information that may involve disk access, and it's called on
the main thread.

For example, ExternalStorageProvider returns the file size and last
modified time when it sees a null projection, and it is calling
File.length() and File.lastModified() for them. In other file providers,
it is also possible for this to trigger a network request for such
information, which will result in a NetworkOnMainThreadException that
crashes the android:ui process.

So to improve stability and performance, we should provide a projection
that only contains the columns that we are actually interested in.

Bug: 218105374
Test: ChooserActivityTest
Change-Id: I91bbd397124f8e4f7d7bc597d59e278e6f54e8bb
This commit is contained in:
Hai Zhang
2022-02-06 11:14:44 +00:00
parent 2ccaf53cd7
commit 5255532157
2 changed files with 11 additions and 5 deletions

View File

@@ -217,6 +217,12 @@ public class ChooserActivity extends ResolverActivity implements
private static final int APP_PREDICTION_SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20;
public static final String APP_PREDICTION_INTENT_FILTER_KEY = "intent_filter";
private static final String[] QUERY_FILE_INFO_PROJECTION = {
OpenableColumns.DISPLAY_NAME,
Downloads.Impl.COLUMN_TITLE,
DocumentsContract.Document.COLUMN_FLAGS
};
private static final String PLURALS_COUNT = "count";
private static final String PLURALS_FILE_NAME = "file_name";
@@ -1474,15 +1480,15 @@ public class ChooserActivity extends ResolverActivity implements
* and to avoid mocking Android core classes.
*/
@VisibleForTesting
public Cursor queryResolver(ContentResolver resolver, Uri uri) {
return resolver.query(uri, null, null, null, null);
public Cursor queryResolver(ContentResolver resolver, String[] projection, Uri uri) {
return resolver.query(uri, projection, null, null, null);
}
private FileInfo extractFileInfo(Uri uri, ContentResolver resolver) {
String fileName = null;
boolean hasThumbnail = false;
try (Cursor cursor = queryResolver(resolver, uri)) {
try (Cursor cursor = queryResolver(resolver, QUERY_FILE_INFO_PROJECTION, uri)) {
if (cursor != null && cursor.getCount() > 0) {
int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int titleIndex = cursor.getColumnIndex(Downloads.Impl.COLUMN_TITLE);

View File

@@ -192,7 +192,7 @@ public class ChooserWrapperActivity extends ChooserActivity implements IChooserW
}
@Override
public Cursor queryResolver(ContentResolver resolver, Uri uri) {
public Cursor queryResolver(ContentResolver resolver, String[] projection, Uri uri) {
if (sOverrides.resolverCursor != null) {
return sOverrides.resolverCursor;
}
@@ -201,7 +201,7 @@ public class ChooserWrapperActivity extends ChooserActivity implements IChooserW
throw new SecurityException("Test exception handling");
}
return super.queryResolver(resolver, uri);
return super.queryResolver(resolver, projection, uri);
}
@Override