Adding user property for CrossProfileContentSharingStrategy.

This property would be used by clients like Sharesheet, DocsUi, etc
to determine how content sharing permissions can be applied to
inter-profile operations, such as sharing Photos, Documents, Media, etc.

Using this, profiles can setup delegated users for content sharing,
originating from that profile. For ex, Private Profile can say that
Cross Profile content sharing to/from it, should be deemed as if it
was originating to/from its parent.

When not sert, the profile itself should be taken as the delegated user
for hanndling cross-profil sharing.

Bug: 307515481
API-Coverage-Bug: 314094348
Test: atest UserManagerServiceUserPropertiesTest
Test: atest UserManagerServiceUserTypeTest
Test: atest FrameworksServicesTests:UserManagerTest
Change-Id: I706ce9bbc7d53c7b734d36a0deb4556c854a5190
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index dc39bea..4c15272 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -4191,12 +4191,15 @@
 
   public final class UserProperties implements android.os.Parcelable {
     method public int describeContents();
+    method public int getCrossProfileContentSharingStrategy();
     method public int getShowInQuietMode();
     method public int getShowInSharingSurfaces();
     method public boolean isCredentialShareableWithParent();
     method public boolean isMediaSharedWithParent();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.UserProperties> CREATOR;
+    field public static final int CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT = 1; // 0x1
+    field public static final int CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION = 0; // 0x0
     field public static final int SHOW_IN_QUIET_MODE_DEFAULT = 2; // 0x2
     field public static final int SHOW_IN_QUIET_MODE_HIDDEN = 1; // 0x1
     field public static final int SHOW_IN_QUIET_MODE_PAUSED = 0; // 0x0
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 98a78cf..c75f63d 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1165,6 +1165,7 @@
   public static final class UserProperties.Builder {
     ctor public UserProperties.Builder();
     method @NonNull public android.content.pm.UserProperties build();
+    method @NonNull public android.content.pm.UserProperties.Builder setCrossProfileContentSharingStrategy(int);
     method @NonNull public android.content.pm.UserProperties.Builder setShowInQuietMode(int);
     method @NonNull public android.content.pm.UserProperties.Builder setShowInSharingSurfaces(int);
   }
diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java
index 56e8291..c401268 100644
--- a/core/java/android/content/pm/UserProperties.java
+++ b/core/java/android/content/pm/UserProperties.java
@@ -69,6 +69,9 @@
     private static final String ATTR_DELETE_APP_WITH_PARENT = "deleteAppWithParent";
     private static final String ATTR_ALWAYS_VISIBLE = "alwaysVisible";
 
+    private static final String ATTR_CROSS_PROFILE_CONTENT_SHARING_STRATEGY =
+            "crossProfileContentSharingStrategy";
+
     /** Index values of each property (to indicate whether they are present in this object). */
     @IntDef(prefix = "INDEX_", value = {
             INDEX_SHOW_IN_LAUNCHER,
@@ -86,6 +89,7 @@
             INDEX_SHOW_IN_QUIET_MODE,
             INDEX_SHOW_IN_SHARING_SURFACES,
             INDEX_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE,
+            INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY
     })
     @Retention(RetentionPolicy.SOURCE)
     private @interface PropertyIndex {
@@ -105,6 +109,7 @@
     private static final int INDEX_SHOW_IN_QUIET_MODE = 12;
     private static final int INDEX_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE = 13;
     private static final int INDEX_SHOW_IN_SHARING_SURFACES = 14;
+    private static final int INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY = 15;
     /** A bit set, mapping each PropertyIndex to whether it is present (1) or absent (0). */
     private long mPropertiesPresent = 0;
 
@@ -365,6 +370,45 @@
      */
     @SuppressLint("UnflaggedApi") // b/306636213
     public static final int SHOW_IN_SHARING_SURFACES_NO = SHOW_IN_LAUNCHER_NO;
+    /**
+     * Possible values for cross profile content sharing strategy for this profile.
+     *
+     * @hide
+     */
+    @IntDef(prefix = {"CROSS_PROFILE_CONTENT_SHARING_STRATEGY_"}, value = {
+            CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION,
+            CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CrossProfileContentSharingStrategy {
+    }
+
+    /**
+     * Signifies that cross-profile content sharing strategy, both to and from this profile, should
+     * not be delegated to any other user/profile.
+     * For ex:
+     * If this property is set for a profile, content sharing applications (such as Android
+     * Sharesheet), should not delegate the decision to share content between that profile and
+     * another profile to whether content sharing is allowed between any other profile/user related
+     * to those profiles. They should instead decide, based upon whether content sharing is
+     * specifically allowed between the two profiles in question.
+     */
+    @SuppressLint("UnflaggedApi") // b/306636213
+    public static final int CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION = 0;
+
+    /**
+     * Signifies that cross-profile content sharing strategy, both to and from this profile, should
+     * be based upon the strategy used by the parent user of the profile.
+     * For ex:
+     * If this property is set for a profile A, content sharing applications (such as Android
+     * Sharesheet), should share content between profile A and profile B, based upon whether content
+     * sharing is allowed between the parent of profile A and profile B.
+     * If it's also set for profile B, then decision should, in turn be made by considering content
+     * sharing strategy between the parents of both profiles.
+     */
+    @SuppressLint("UnflaggedApi") // b/306636213
+    public static final int CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT = 1;
+
 
     /**
      * Creates a UserProperties (intended for the SystemServer) that stores a reference to the given
@@ -423,6 +467,7 @@
         setCredentialShareableWithParent(orig.isCredentialShareableWithParent());
         setShowInQuietMode(orig.getShowInQuietMode());
         setShowInSharingSurfaces(orig.getShowInSharingSurfaces());
+        setCrossProfileContentSharingStrategy(orig.getCrossProfileContentSharingStrategy());
     }
 
     /**
@@ -776,8 +821,7 @@
     private @CrossProfileIntentFilterAccessControlLevel int mCrossProfileIntentFilterAccessControl;
 
     /**
-     * Returns the user's {@link CrossProfileIntentResolutionStrategy}. If not explicitly
-     * configured, default value is {@link #CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_DEFAULT}.
+     * Returns the user's {@link CrossProfileIntentResolutionStrategy}.
      * @return user's {@link CrossProfileIntentResolutionStrategy}.
      *
      * @hide
@@ -792,11 +836,8 @@
         throw new SecurityException("You don't have permission to query "
                 + "crossProfileIntentResolutionStrategy");
     }
-    /**
-     * Sets {@link CrossProfileIntentResolutionStrategy} for the user.
-     * @param val resolution strategy for user
-     * @hide
-     */
+
+    /** @hide */
     public void setCrossProfileIntentResolutionStrategy(
             @CrossProfileIntentResolutionStrategy int val) {
         this.mCrossProfileIntentResolutionStrategy = val;
@@ -804,6 +845,39 @@
     }
     private @CrossProfileIntentResolutionStrategy int mCrossProfileIntentResolutionStrategy;
 
+    /**
+     * Returns the user's {@link CrossProfileContentSharingStrategy}.
+     *
+     * Content sharing applications, such as Android Sharesheet allow sharing of content
+     * (an image, for ex.) between profiles, based upon cross-profile access checks between the
+     * originating and destined profile.
+     * In some cases however, we may want another user (such as profile parent) to serve as the
+     * delegated user to be used for such checks.
+     * To effect the same, clients can fetch this property and accordingly replace the
+     * originating/destined profile by another user for cross-profile access checks.
+     *
+     * @return user's {@link CrossProfileContentSharingStrategy}.
+     */
+    @SuppressLint("UnflaggedApi")  // b/306636213
+    public @CrossProfileContentSharingStrategy int getCrossProfileContentSharingStrategy() {
+        if (isPresent(INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY)) {
+            return mCrossProfileContentSharingStrategy;
+        }
+        if (mDefaultProperties != null) {
+            return mDefaultProperties.mCrossProfileContentSharingStrategy;
+        }
+        throw new SecurityException("You don't have permission to query "
+                + "crossProfileContentSharingStrategy");
+    }
+
+    /** @hide */
+    public void setCrossProfileContentSharingStrategy(
+            @CrossProfileContentSharingStrategy int val) {
+        this.mCrossProfileContentSharingStrategy = val;
+        setPresent(INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY);
+    }
+    private @CrossProfileContentSharingStrategy int mCrossProfileContentSharingStrategy;
+
 
     @Override
     public String toString() {
@@ -827,6 +901,7 @@
                 + isAuthAlwaysRequiredToDisableQuietMode()
                 + ", mDeleteAppWithParent=" + getDeleteAppWithParent()
                 + ", mAlwaysVisible=" + getAlwaysVisible()
+                + ", mCrossProfileContentSharingStrategy=" + getCrossProfileContentSharingStrategy()
                 + "}";
     }
 
@@ -856,6 +931,8 @@
                 + isAuthAlwaysRequiredToDisableQuietMode());
         pw.println(prefix + "    mDeleteAppWithParent=" + getDeleteAppWithParent());
         pw.println(prefix + "    mAlwaysVisible=" + getAlwaysVisible());
+        pw.println(prefix + "    mCrossProfileContentSharingStrategy="
+                + getCrossProfileContentSharingStrategy());
     }
 
     /**
@@ -934,6 +1011,8 @@
                 case ATTR_ALWAYS_VISIBLE:
                     setAlwaysVisible(parser.getAttributeBoolean(i));
                     break;
+                case ATTR_CROSS_PROFILE_CONTENT_SHARING_STRATEGY:
+                    setCrossProfileContentSharingStrategy(parser.getAttributeInt(i));
                 default:
                     Slog.w(LOG_TAG, "Skipping unknown property " + attributeName);
             }
@@ -1008,6 +1087,10 @@
             serializer.attributeBoolean(null, ATTR_ALWAYS_VISIBLE,
                     mAlwaysVisible);
         }
+        if (isPresent(INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY)) {
+            serializer.attributeInt(null, ATTR_CROSS_PROFILE_CONTENT_SHARING_STRATEGY,
+                    mCrossProfileContentSharingStrategy);
+        }
     }
 
     // For use only with an object that has already had any permission-lacking fields stripped out.
@@ -1029,6 +1112,7 @@
         dest.writeBoolean(mAuthAlwaysRequiredToDisableQuietMode);
         dest.writeBoolean(mDeleteAppWithParent);
         dest.writeBoolean(mAlwaysVisible);
+        dest.writeInt(mCrossProfileContentSharingStrategy);
     }
 
     /**
@@ -1054,6 +1138,7 @@
         mAuthAlwaysRequiredToDisableQuietMode = source.readBoolean();
         mDeleteAppWithParent = source.readBoolean();
         mAlwaysVisible = source.readBoolean();
+        mCrossProfileContentSharingStrategy = source.readInt();
     }
 
     @Override
@@ -1100,6 +1185,8 @@
         private boolean mAuthAlwaysRequiredToDisableQuietMode = false;
         private boolean mDeleteAppWithParent = false;
         private boolean mAlwaysVisible = false;
+        private @CrossProfileContentSharingStrategy int mCrossProfileContentSharingStrategy =
+                CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION;
 
         /**
          * @hide
@@ -1231,6 +1318,19 @@
             return this;
         }
 
+        /** Sets the value for {@link #mCrossProfileContentSharingStrategy}
+         * @hide
+         */
+
+        @TestApi
+        @SuppressLint("UnflaggedApi") // b/306636213
+        @NonNull
+        public Builder setCrossProfileContentSharingStrategy(@CrossProfileContentSharingStrategy
+                int crossProfileContentSharingStrategy) {
+            mCrossProfileContentSharingStrategy = crossProfileContentSharingStrategy;
+            return this;
+        }
+
         /** Builds a UserProperties object with *all* values populated.
          * @hide
          */
@@ -1253,7 +1353,8 @@
                     mCredentialShareableWithParent,
                     mAuthAlwaysRequiredToDisableQuietMode,
                     mDeleteAppWithParent,
-                    mAlwaysVisible);
+                    mAlwaysVisible,
+                    mCrossProfileContentSharingStrategy);
         }
     } // end Builder
 
@@ -1272,7 +1373,8 @@
             boolean credentialShareableWithParent,
             boolean authAlwaysRequiredToDisableQuietMode,
             boolean deleteAppWithParent,
-            boolean alwaysVisible) {
+            boolean alwaysVisible,
+            @CrossProfileContentSharingStrategy int crossProfileContentSharingStrategy) {
         mDefaultProperties = null;
         setShowInLauncher(showInLauncher);
         setStartWithParent(startWithParent);
@@ -1290,5 +1392,6 @@
                 authAlwaysRequiredToDisableQuietMode);
         setDeleteAppWithParent(deleteAppWithParent);
         setAlwaysVisible(alwaysVisible);
+        setCrossProfileContentSharingStrategy(crossProfileContentSharingStrategy);
     }
 }
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 4ef8cb7..7f013b8 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -160,7 +160,9 @@
                                 UserProperties.SHOW_IN_SHARING_SURFACES_WITH_PARENT)
                         .setMediaSharedWithParent(true)
                         .setCredentialShareableWithParent(true)
-                        .setDeleteAppWithParent(true));
+                        .setDeleteAppWithParent(true)
+                        .setCrossProfileContentSharingStrategy(UserProperties
+                                .CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT));
     }
 
     /**
@@ -318,7 +320,9 @@
                                 UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE)
                         .setCrossProfileIntentFilterAccessControl(
                                 UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM)
-                        .setInheritDevicePolicy(UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT));
+                        .setInheritDevicePolicy(UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT)
+                        .setCrossProfileContentSharingStrategy(
+                                UserProperties.CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT));
     }
 
     /**
diff --git a/services/tests/servicestests/res/xml/usertypes_test_profile.xml b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
index e89199d..9b047f2 100644
--- a/services/tests/servicestests/res/xml/usertypes_test_profile.xml
+++ b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
@@ -45,6 +45,7 @@
             inheritDevicePolicy='450'
             deleteAppWithParent='false'
             alwaysVisible='true'
+            crossProfileContentSharingStrategy='0'
         />
     </profile-type>
     <profile-type name='custom.test.1' max-allowed-per-parent='14' />
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
index d70a4fd..72cc969 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
@@ -71,6 +71,7 @@
                 .setAuthAlwaysRequiredToDisableQuietMode(false)
                 .setDeleteAppWithParent(false)
                 .setAlwaysVisible(false)
+                .setCrossProfileContentSharingStrategy(0)
                 .build();
         final UserProperties actualProps = new UserProperties(defaultProps);
         actualProps.setShowInLauncher(14);
@@ -86,6 +87,7 @@
         actualProps.setAuthAlwaysRequiredToDisableQuietMode(true);
         actualProps.setDeleteAppWithParent(true);
         actualProps.setAlwaysVisible(true);
+        actualProps.setCrossProfileContentSharingStrategy(1);
 
         // Write the properties to xml.
         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -199,6 +201,8 @@
                 copy::isMediaSharedWithParent, true);
         assertEqualGetterOrThrows(orig::isCredentialShareableWithParent,
                 copy::isCredentialShareableWithParent, true);
+        assertEqualGetterOrThrows(orig::getCrossProfileContentSharingStrategy,
+                copy::getCrossProfileContentSharingStrategy, true);
     }
 
     /**
@@ -256,5 +260,7 @@
                 .isEqualTo(actual.isAuthAlwaysRequiredToDisableQuietMode());
         assertThat(expected.getDeleteAppWithParent()).isEqualTo(actual.getDeleteAppWithParent());
         assertThat(expected.getAlwaysVisible()).isEqualTo(actual.getAlwaysVisible());
+        assertThat(expected.getCrossProfileContentSharingStrategy())
+                .isEqualTo(actual.getCrossProfileContentSharingStrategy());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index 77f6939..d0ad573 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -29,7 +29,6 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertTrue;
@@ -96,7 +95,8 @@
                 .setShowInQuietMode(30)
                 .setInheritDevicePolicy(340)
                 .setDeleteAppWithParent(true)
-                .setAlwaysVisible(true);
+                .setAlwaysVisible(true)
+                .setCrossProfileContentSharingStrategy(1);
 
         final UserTypeDetails type = new UserTypeDetails.Builder()
                 .setName("a.name")
@@ -175,6 +175,8 @@
                 .getInheritDevicePolicy());
         assertTrue(type.getDefaultUserPropertiesReference().getDeleteAppWithParent());
         assertTrue(type.getDefaultUserPropertiesReference().getAlwaysVisible());
+        assertEquals(1, type.getDefaultUserPropertiesReference()
+                .getCrossProfileContentSharingStrategy());
 
         assertEquals(23, type.getBadgeLabel(0));
         assertEquals(24, type.getBadgeLabel(1));
@@ -231,6 +233,8 @@
         assertEquals(UserProperties.SHOW_IN_LAUNCHER_SEPARATE, props.getShowInSharingSurfaces());
         assertEquals(UserProperties.SHOW_IN_QUIET_MODE_PAUSED,
                 props.getShowInQuietMode());
+        assertEquals(UserProperties.CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION,
+                props.getCrossProfileContentSharingStrategy());
 
         assertFalse(type.hasBadge());
     }
@@ -323,7 +327,8 @@
                 .setShowInSharingSurfaces(22)
                 .setShowInQuietMode(24)
                 .setDeleteAppWithParent(true)
-                .setAlwaysVisible(false);
+                .setAlwaysVisible(false)
+                .setCrossProfileContentSharingStrategy(1);
 
         final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
         builders.put(userTypeAosp1, new UserTypeDetails.Builder()
@@ -370,6 +375,8 @@
                 aospType.getDefaultUserPropertiesReference().getShowInQuietMode());
         assertTrue(aospType.getDefaultUserPropertiesReference().getDeleteAppWithParent());
         assertFalse(aospType.getDefaultUserPropertiesReference().getAlwaysVisible());
+        assertEquals(1, aospType.getDefaultUserPropertiesReference()
+                .getCrossProfileContentSharingStrategy());
 
         // userTypeAosp2 should be modified.
         aospType = builders.get(userTypeAosp2).createUserTypeDetails();
@@ -422,6 +429,8 @@
                 .getInheritDevicePolicy());
         assertFalse(aospType.getDefaultUserPropertiesReference().getDeleteAppWithParent());
         assertTrue(aospType.getDefaultUserPropertiesReference().getAlwaysVisible());
+        assertEquals(0, aospType.getDefaultUserPropertiesReference()
+                .getCrossProfileContentSharingStrategy());
 
         // userTypeOem1 should be created.
         UserTypeDetails.Builder customType = builders.get(userTypeOem1);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 8933c6c..ced0bb5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -23,7 +23,6 @@
 import static org.junit.Assume.assumeTrue;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertThrows;
-import static org.testng.Assert.assertTrue;
 
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
@@ -219,6 +218,8 @@
                 .isEqualTo(cloneUserProperties.isMediaSharedWithParent());
         assertThat(typeProps.isCredentialShareableWithParent())
                 .isEqualTo(cloneUserProperties.isCredentialShareableWithParent());
+        assertThat(typeProps.getCrossProfileContentSharingStrategy())
+                .isEqualTo(cloneUserProperties.getCrossProfileContentSharingStrategy());
         assertThrows(SecurityException.class, cloneUserProperties::getDeleteAppWithParent);
         assertThrows(SecurityException.class, cloneUserProperties::getAlwaysVisible);
         compareDrawables(mUserManager.getUserBadge(),
@@ -338,6 +339,8 @@
         assertThat(typeProps.isAuthAlwaysRequiredToDisableQuietMode())
                 .isEqualTo(privateProfileUserProperties
                         .isAuthAlwaysRequiredToDisableQuietMode());
+        assertThat(typeProps.getCrossProfileContentSharingStrategy())
+                .isEqualTo(privateProfileUserProperties.getCrossProfileContentSharingStrategy());
         assertThrows(SecurityException.class, privateProfileUserProperties::getDeleteAppWithParent);
         compareDrawables(mUserManager.getUserBadge(),
                 Resources.getSystem().getDrawable(userTypeDetails.getBadgePlain()));