From 5255532157be0e85dbbe865bb01a4077ba6ffdca Mon Sep 17 00:00:00 2001 From: Hai Zhang Date: Sun, 6 Feb 2022 11:14:44 +0000 Subject: [PATCH] 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 --- .../com/android/internal/app/ChooserActivity.java | 12 +++++++++--- .../android/internal/app/ChooserWrapperActivity.java | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 4ae6bf7e8379d..150eb65269086 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -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); diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java index 7f8598217ec69..139bc36156ad5 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java @@ -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