Merge "Fix Android/media detection bug" into sc-mainline-prod
diff --git a/src/com/android/providers/media/PickerUriResolver.java b/src/com/android/providers/media/PickerUriResolver.java
index a104068..c9bd138 100644
--- a/src/com/android/providers/media/PickerUriResolver.java
+++ b/src/com/android/providers/media/PickerUriResolver.java
@@ -93,7 +93,14 @@
 
         checkUriPermission(uri, callingPid, callingUid);
 
-        final ContentResolver resolver = getContentResolverForUserId(uri);
+        final ContentResolver resolver;
+        try {
+            resolver = getContentResolverForUserId(uri);
+        } catch (IllegalArgumentException e) {
+            // This is to be consistent with MediaProvider's response when a file is not found.
+            Log.e(TAG, "No item at " + uri, e);
+            throw new FileNotFoundException("No item at " + uri);
+        }
         if (PickerDbFacade.isPickerDbEnabled()) {
             if (canHandleUriInUser(uri)) {
                 return openPickerFile(uri);
@@ -115,7 +122,14 @@
             throws FileNotFoundException {
         checkUriPermission(uri, callingPid, callingUid);
 
-        final ContentResolver resolver = getContentResolverForUserId(uri);
+        final ContentResolver resolver;
+        try {
+            resolver = getContentResolverForUserId(uri);
+        } catch (IllegalArgumentException e) {
+            // This is to be consistent with MediaProvider's response when a file is not found.
+            Log.e(TAG, "No item at " + uri, e);
+            throw new FileNotFoundException("No item at " + uri);
+        }
         if (PickerDbFacade.isPickerDbEnabled()) {
             if (canHandleUriInUser(uri)) {
                 return new AssetFileDescriptor(openPickerFile(uri), 0,
@@ -139,8 +153,10 @@
 
         try {
             return queryInternal(uri, projection, queryArgs, signal);
-        } catch (FileNotFoundException e) {
-            Log.d(TAG, "File not found for uri: " + uri, e);
+        } catch (IllegalArgumentException e) {
+            // This is to be consistent with MediaProvider, it returns an empty cursor if the row
+            // does not exist.
+            Log.e(TAG, "File not found for uri: " + uri, e);
             return new MatrixCursor(projection == null ? new String[] {} : projection);
         }
     }
@@ -148,7 +164,7 @@
     // TODO(b/191362529): Restrict projection values when we start querying picker db.
     // Add PickerColumns and add checks for projection.
     private Cursor queryInternal(Uri uri, String[] projection, Bundle queryArgs,
-            CancellationSignal signal) throws FileNotFoundException {
+            CancellationSignal signal) {
         final ContentResolver resolver = getContentResolverForUserId(uri);
 
         if (PickerDbFacade.isPickerDbEnabled()) {
@@ -179,8 +195,6 @@
                 return getCursorString(cursor,
                         CloudMediaProviderContract.MediaColumns.MIME_TYPE);
             }
-        } catch (FileNotFoundException e) {
-            throw new IllegalArgumentException(e.getMessage());
         }
 
         throw new IllegalArgumentException("Failed to getType for uri: " + uri);
@@ -334,13 +348,12 @@
     }
 
     @VisibleForTesting
-    ContentResolver getContentResolverForUserId(Uri uri) throws FileNotFoundException {
-        final UserId userId = UserId.of(UserHandle.of(getUserId(uri)));
+    ContentResolver getContentResolverForUserId(Uri uri) {
+            final UserId userId = UserId.of(UserHandle.of(getUserId(uri)));
         try {
             return userId.getContentResolver(mContext);
         } catch (NameNotFoundException e) {
-            throw new FileNotFoundException("File not found due to unavailable content resolver "
-                    + "for uri: " + uri + " ; error: " + e);
+            throw new IllegalArgumentException("Cannot find content resolver for uri: " + uri, e);
         }
     }
 }
diff --git a/src/com/android/providers/media/photopicker/data/ItemsProvider.java b/src/com/android/providers/media/photopicker/data/ItemsProvider.java
index 8e62c8e..38f594a 100644
--- a/src/com/android/providers/media/photopicker/data/ItemsProvider.java
+++ b/src/com/android/providers/media/photopicker/data/ItemsProvider.java
@@ -31,12 +31,15 @@
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.provider.CloudMediaProviderContract.AlbumColumns;
 import android.provider.MediaStore;
 import android.provider.MediaStore.Files.FileColumns;
 import android.provider.MediaStore.MediaColumns;
+import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.modules.utils.build.SdkLevel;
 import com.android.providers.media.PickerUriResolver;
 import com.android.providers.media.photopicker.PickerSyncController;
 import com.android.providers.media.photopicker.data.model.Category;
@@ -304,8 +307,48 @@
 
         if (userId.equals(UserId.CURRENT_USER)) {
             return uri;
-        } else {
-            return ContentProvider.createContentUriForUser(uri, userId.getUserHandle());
         }
+
+        return createContentUriForUser(uri, userId.getUserHandle());
+    }
+
+    private static Uri createContentUriForUser(Uri uri, UserHandle userHandle) {
+        if (SdkLevel.isAtLeastS()) {
+            return ContentProvider.createContentUriForUser(uri, userHandle);
+        }
+
+        return createContentUriForUserImpl(uri, userHandle);
+    }
+
+    /**
+     * This method is a copy of {@link ContentProvider#createContentUriForUser(Uri, UserHandle)}
+     * which is a System API added in Android S.
+     */
+    private static Uri createContentUriForUserImpl(Uri uri, UserHandle userHandle) {
+        if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
+            throw new IllegalArgumentException(String.format(
+                    "Given URI [%s] is not a content URI: ", uri));
+        }
+
+        int userId = userHandle.getIdentifier();
+        if (uriHasUserId(uri)) {
+            if (String.valueOf(userId).equals(uri.getUserInfo())) {
+                return uri;
+            }
+            throw new IllegalArgumentException(String.format(
+                    "Given URI [%s] already has a user ID, different from given user handle [%s]",
+                    uri,
+                    userId));
+        }
+
+        Uri.Builder builder = uri.buildUpon();
+        builder.encodedAuthority(
+                "" + userHandle.getIdentifier() + "@" + uri.getEncodedAuthority());
+        return builder.build();
+    }
+
+    private static boolean uriHasUserId(Uri uri) {
+        if (uri == null) return false;
+        return !TextUtils.isEmpty(uri.getUserInfo());
     }
 }
diff --git a/tests/src/com/android/providers/media/PickerUriResolverTest.java b/tests/src/com/android/providers/media/PickerUriResolverTest.java
index fb78ef3..491d9f2 100644
--- a/tests/src/com/android/providers/media/PickerUriResolverTest.java
+++ b/tests/src/com/android/providers/media/PickerUriResolverTest.java
@@ -392,9 +392,7 @@
             fail("Invalid user specified in the picker uri: " + uri);
         } catch (FileNotFoundException expected) {
             // expected
-            assertThat(expected.getMessage()).isEqualTo("File not found due to unavailable content"
-                    + " resolver for uri: " + uri
-                    + " ; error: android.content.pm.PackageManager$NameNotFoundException");
+            assertThat(expected.getMessage()).isEqualTo("No item at " + uri);
         }
     }
 
@@ -405,9 +403,7 @@
             fail("Invalid user specified in the picker uri: " + uri);
         } catch (FileNotFoundException expected) {
             // expected
-            assertThat(expected.getMessage()).isEqualTo("File not found due to unavailable content"
-                    + " resolver for uri: " + uri
-                    + " ; error: android.content.pm.PackageManager$NameNotFoundException");
+            assertThat(expected.getMessage()).isEqualTo("No item at " + uri);
         }
     }
 
@@ -424,9 +420,8 @@
             fail("Invalid user specified in the picker uri: " + uri);
         } catch (IllegalArgumentException expected) {
             // expected
-            assertThat(expected.getMessage()).isEqualTo("File not found due to unavailable "
-                    + "content resolver for uri: " + uri
-                    + " ; error: android.content.pm.PackageManager$NameNotFoundException");
+            assertThat(expected.getMessage()).isEqualTo("Cannot find content resolver for uri: "
+                    + uri);
         }
     }
 
diff --git a/tests/src/com/android/providers/media/photopicker/data/model/ItemTest.java b/tests/src/com/android/providers/media/photopicker/data/model/ItemTest.java
index 656c7df..81fec5d 100644
--- a/tests/src/com/android/providers/media/photopicker/data/model/ItemTest.java
+++ b/tests/src/com/android/providers/media/photopicker/data/model/ItemTest.java
@@ -69,8 +69,6 @@
     }
 
     @Test
-    // TODO(b/205096638) Re-enable if we add work profile support on R
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
     public void testConstructor_differentUser() {
         final String id = "1";
         final long dateTaken = 12345678L;