Add policy for nearby notification streaming

Notification streaming is sending notification data from pre-installed apps to nearby devices. The policy specifies options in which notification streaming is supported. The default policy is streaming notifications only to devices with the same managed account.

Bug: 179910174
Test: Builds successfully
Change-Id: I96a9c67aab1d27eb0527add51adc019bd98b64b4
diff --git a/core/api/current.txt b/core/api/current.txt
index 935cf70..06e3f88 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -7128,6 +7128,7 @@
     method public int getMaximumFailedPasswordsForWipe(@Nullable android.content.ComponentName);
     method public long getMaximumTimeToLock(@Nullable android.content.ComponentName);
     method @NonNull public java.util.List<java.lang.String> getMeteredDataDisabledPackages(@NonNull android.content.ComponentName);
+    method public int getNearbyNotificationStreamingPolicy();
     method @Deprecated @ColorInt public int getOrganizationColor(@NonNull android.content.ComponentName);
     method @Nullable public CharSequence getOrganizationName(@NonNull android.content.ComponentName);
     method public java.util.List<android.telephony.data.ApnSetting> getOverrideApns(@NonNull android.content.ComponentName);
@@ -7271,6 +7272,7 @@
     method public void setMaximumFailedPasswordsForWipe(@NonNull android.content.ComponentName, int);
     method public void setMaximumTimeToLock(@NonNull android.content.ComponentName, long);
     method @NonNull public java.util.List<java.lang.String> setMeteredDataDisabledPackages(@NonNull android.content.ComponentName, @NonNull java.util.List<java.lang.String>);
+    method public void setNearbyNotificationStreamingPolicy(int);
     method public void setNetworkLoggingEnabled(@Nullable android.content.ComponentName, boolean);
     method @Deprecated public void setOrganizationColor(@NonNull android.content.ComponentName, int);
     method public void setOrganizationId(@NonNull String);
@@ -7439,6 +7441,9 @@
     field public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 0x1
     field public static final int MAKE_USER_EPHEMERAL = 2; // 0x2
     field public static final String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning";
+    field public static final int NEARBY_STREAMING_DISABLED = 0; // 0x0
+    field public static final int NEARBY_STREAMING_ENABLED = 1; // 0x1
+    field public static final int NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY = 2; // 0x2
     field public static final int OPERATION_SAFETY_REASON_DRIVING_DISTRACTION = 1; // 0x1
     field public static final int PASSWORD_COMPLEXITY_HIGH = 327680; // 0x50000
     field public static final int PASSWORD_COMPLEXITY_LOW = 65536; // 0x10000
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 26e6741..6de7b5c 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1675,6 +1675,29 @@
     })
     public @interface PasswordComplexity {}
 
+    /** Indicates that nearby streaming is disabled. */
+    public static final int NEARBY_STREAMING_DISABLED = 0;
+
+    /** Indicates that nearby streaming is enabled. */
+    public static final int NEARBY_STREAMING_ENABLED = 1;
+
+    /**
+     * Indicates that nearby streaming is enabled only to devices offering a comparable level of
+     * security, with the same authenticated managed account.
+     */
+    public static final int NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY = 2;
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"NEARBY_STREAMING_"}, value = {
+        NEARBY_STREAMING_DISABLED,
+        NEARBY_STREAMING_ENABLED,
+        NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY,
+    })
+    public @interface NearbyStreamingPolicy {}
+
     /**
      * Activity action: have the user enter a new password for the parent profile.
      * If the intent is launched from within a managed profile, this will trigger
@@ -7163,6 +7186,41 @@
     }
 
     /**
+     * Called by a device/profile owner to set nearby notification streaming policy. Notification
+     * streaming is sending notification data from pre-installed apps to nearby devices.
+     *
+     * @param policy One of the {@code NearbyStreamingPolicy} constants.
+     * @throws SecurityException if caller is not a device or profile owner
+     */
+    public void setNearbyNotificationStreamingPolicy(@NearbyStreamingPolicy int policy) {
+        throwIfParentInstance("setNearbyNotificationStreamingPolicy");
+        if (mService == null) {
+            return;
+        }
+        try {
+            mService.setNearbyNotificationStreamingPolicy(policy);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the current runtime nearby notification streaming policy set by the device or profile
+     * owner. The default is {@link #NEARBY_STREAMING_DISABLED}.
+     */
+    public @NearbyStreamingPolicy int getNearbyNotificationStreamingPolicy() {
+        throwIfParentInstance("getNearbyNotificationStreamingPolicy");
+        if (mService == null) {
+            return NEARBY_STREAMING_DISABLED;
+        }
+        try {
+            return mService.getNearbyNotificationStreamingPolicy();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Called by a device owner, or alternatively a profile owner from Android 8.0 (API level 26) or
      * higher, to set whether auto time is required. If auto time is required, no user will be able
      * set the date and time and network date and time will be used.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 5e49a98..394e53f 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -132,6 +132,9 @@
     void setScreenCaptureDisabled(in ComponentName who, boolean disabled, boolean parent);
     boolean getScreenCaptureDisabled(in ComponentName who, int userHandle, boolean parent);
 
+    void setNearbyNotificationStreamingPolicy(int policy);
+    int getNearbyNotificationStreamingPolicy();
+
     void setKeyguardDisabledFeatures(in ComponentName who, int which, boolean parent);
     int getKeyguardDisabledFeatures(in ComponentName who, int userHandle, boolean parent);
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 90755b9..aea7df4 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5681,6 +5681,12 @@
     <permission android:name="android.permission.RENOUNCE_PERMISSIONS"
                 android:protectionLevel="signature|privileged" />
 
+    <!-- Allows an application to read nearby streaming policy. The policy allows the device
+         to stream its notifications and apps to nearby devices.
+         @hide -->
+    <permission android:name="android.permission.READ_NEARBY_STREAMING_POLICY"
+        android:protectionLevel="signature|privileged" />
+
     <!-- @SystemApi Allows the holder to set the source of the data when setting a clip on the
          clipboard.
          @hide -->
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 56e2385..f86ca92 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -16,6 +16,7 @@
 
 package com.android.server.devicepolicy;
 
+import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_DISABLED;
 import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
 
@@ -69,6 +70,8 @@
             "disable-bt-contacts-sharing";
     private static final String TAG_DISABLE_SCREEN_CAPTURE = "disable-screen-capture";
     private static final String TAG_DISABLE_ACCOUNT_MANAGEMENT = "disable-account-management";
+    private static final String TAG_NEARBY_NOTIFICATION_STREAMING_POLICY =
+            "nearby-notification-streaming-policy";
     private static final String TAG_REQUIRE_AUTO_TIME = "require_auto_time";
     private static final String TAG_FORCE_EPHEMERAL_USERS = "force_ephemeral_users";
     private static final String TAG_IS_NETWORK_LOGGING_ENABLED = "is_network_logging_enabled";
@@ -159,6 +162,9 @@
     @DevicePolicyManager.PasswordComplexity
     int mPasswordComplexity = PASSWORD_COMPLEXITY_NONE;
 
+    @DevicePolicyManager.NearbyStreamingPolicy
+    int mNearbyNotificationStreamingPolicy = NEARBY_STREAMING_DISABLED;
+
     @Nullable
     FactoryResetProtectionPolicy mFactoryResetProtectionPolicy = null;
 
@@ -549,6 +555,10 @@
         if (mPasswordComplexity != PASSWORD_COMPLEXITY_NONE) {
             writeAttributeValueToXml(out, TAG_PASSWORD_COMPLEXITY, mPasswordComplexity);
         }
+        if (mNearbyNotificationStreamingPolicy != NEARBY_STREAMING_DISABLED) {
+            writeAttributeValueToXml(out, TAG_NEARBY_NOTIFICATION_STREAMING_POLICY,
+                    mNearbyNotificationStreamingPolicy);
+        }
         if (!TextUtils.isEmpty(mOrganizationId)) {
             writeTextToXml(out, TAG_ORGANIZATION_ID, mOrganizationId);
         }
@@ -794,6 +804,8 @@
                 mCommonCriteriaMode = parser.getAttributeBoolean(null, ATTR_VALUE, false);
             } else if (TAG_PASSWORD_COMPLEXITY.equals(tag)) {
                 mPasswordComplexity = parser.getAttributeInt(null, ATTR_VALUE);
+            } else if (TAG_NEARBY_NOTIFICATION_STREAMING_POLICY.equals(tag)) {
+                mNearbyNotificationStreamingPolicy = parser.getAttributeInt(null, ATTR_VALUE);
             } else if (TAG_ORGANIZATION_ID.equals(tag)) {
                 type = parser.next();
                 if (type == TypedXmlPullParser.TEXT) {
@@ -1154,6 +1166,9 @@
         pw.print("mPasswordComplexity=");
         pw.println(mPasswordComplexity);
 
+        pw.print("mNearbyNotificationStreamingPolicy=");
+        pw.println(mNearbyNotificationStreamingPolicy);
+
         if (!TextUtils.isEmpty(mOrganizationId)) {
             pw.print("mOrganizationId=");
             pw.println(mOrganizationId);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 8739a01..34f6353 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -63,6 +63,7 @@
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
+import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_DISABLED;
 import static android.app.admin.DevicePolicyManager.NON_ORG_OWNED_PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
 import static android.app.admin.DevicePolicyManager.OPERATION_SAFETY_REASON_NONE;
 import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
@@ -7472,6 +7473,42 @@
         });
     }
 
+    @Override
+    public void setNearbyNotificationStreamingPolicy(int policy) {
+        if (!mHasFeature) {
+            return;
+        }
+
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+
+        synchronized (getLockObject()) {
+            final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
+            if (admin.mNearbyNotificationStreamingPolicy != policy) {
+                admin.mNearbyNotificationStreamingPolicy = policy;
+                saveSettingsLocked(caller.getUserId());
+            }
+        }
+    }
+
+    @Override
+    public int getNearbyNotificationStreamingPolicy() {
+        if (!mHasFeature) {
+            return NEARBY_STREAMING_DISABLED;
+        }
+
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(
+                isDeviceOwner(caller)
+                    || isProfileOwner(caller)
+                    || hasCallingOrSelfPermission(permission.READ_NEARBY_STREAMING_POLICY));
+
+        synchronized (getLockObject()) {
+            final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
+            return admin.mNearbyNotificationStreamingPolicy;
+        }
+    }
+
     /**
      * Set whether auto time is required by the specified admin (must be device or profile owner).
      */