Merge "Expose create uri as user as system api." into sc-dev
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 64f56bf3..a140e8a 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -55,6 +55,10 @@
 
 package android.content {
 
+  public abstract class ContentProvider implements android.content.ComponentCallbacks2 {
+    method @NonNull public static android.net.Uri createContentUriAsUser(@NonNull android.net.Uri, @NonNull android.os.UserHandle);
+  }
+
   public abstract class Context {
     method @NonNull public android.os.UserHandle getUser();
   }
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 49248b5..73b4f62 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -2621,6 +2621,48 @@
         return !TextUtils.isEmpty(uri.getUserInfo());
     }
 
+    /**
+     * Returns the given content URI explicitly associated with the given {@link UserHandle}.
+     *
+     * @param contentUri The content URI to be associated with a user handle.
+     * @param userHandle The user handle with which to associate the URI.
+     *
+     * @throws IllegalArgumentException if
+     * <ul>
+     *  <li>the given URI is not content URI (a content URI has {@link Uri#getScheme} equal to
+     *  {@link ContentResolver.SCHEME_CONTENT}) or</li>
+     *  <li>the given URI is already explicitly associated with a {@link UserHandle}, which is
+     *  different than the given one.</li>
+     *  </ul>
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static Uri createContentUriAsUser(
+            @NonNull Uri contentUri, @NonNull UserHandle userHandle) {
+        if (!ContentResolver.SCHEME_CONTENT.equals(contentUri.getScheme())) {
+            throw new IllegalArgumentException(String.format(
+                "Given URI [%s] is not a content URI: ", contentUri));
+        }
+
+        int userId = userHandle.getIdentifier();
+        if (uriHasUserId(contentUri)) {
+            if (String.valueOf(userId).equals(contentUri.getUserInfo())) {
+                return contentUri;
+            }
+            throw new IllegalArgumentException(String.format(
+                "Given URI [%s] already has a user ID, different from given user handle [%s]",
+                contentUri,
+                userId));
+        }
+
+        Uri.Builder builder = contentUri.buildUpon();
+        builder.encodedAuthority(
+                "" + userHandle.getIdentifier() + "@" + contentUri.getEncodedAuthority());
+        return builder.build();
+    }
+
     /** @hide */
     @UnsupportedAppUsage
     public static Uri maybeAddUserId(Uri uri, int userId) {
diff --git a/core/tests/coretests/src/android/content/ContentProviderTest.java b/core/tests/coretests/src/android/content/ContentProviderTest.java
index 8895f9b..b282064 100644
--- a/core/tests/coretests/src/android/content/ContentProviderTest.java
+++ b/core/tests/coretests/src/android/content/ContentProviderTest.java
@@ -23,6 +23,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ProviderInfo;
 import android.net.Uri;
+import android.os.UserHandle;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -86,4 +87,11 @@
                 mCp.validateIncomingUri(
                         Uri.parse("content://com.example/foo/bar?foo=b//ar#foo=b//ar")));
     }
+
+    @Test
+    public void testCreateContentUriAsUser() {
+        Uri uri = Uri.parse("content://com.example/foo/bar");
+        Uri expectedUri = Uri.parse("content://7@com.example/foo/bar");
+        assertEquals(expectedUri, ContentProvider.createContentUriAsUser(uri, UserHandle.of(7)));
+    }
 }