From e567ca6860f19c3dbe3c1cb3b7952c489f971028 Mon Sep 17 00:00:00 2001
From: Anthony Alridge <anthonyalridge@google.com>
Date: Mon, 30 Jan 2023 17:03:27 +0000
Subject: [PATCH] Show notification when work profile switched off

This will notify the user if phone calls and messages
are unavailable.
Screenshots in bug

Bug: 262720433
Test: Manually tested on Pixel 6a
Change-Id: I060c3fa225fe1cf53344d9f8b634e8afbbab468d
---
 .../app/admin/DevicePolicyResources.java      | 21 +++++
 core/res/res/drawable/ic_phone_disabled.xml   | 25 +++++
 core/res/res/values/strings.xml               | 10 ++
 core/res/res/values/symbols.xml               |  4 +
 proto/src/system_messages.proto               |  6 ++
 .../DevicePolicyManagerService.java           | 91 ++++++++++++++++++-
 6 files changed, 156 insertions(+), 1 deletion(-)
 create mode 100644 core/res/res/drawable/ic_phone_disabled.xml

diff --git a/core/java/android/app/admin/DevicePolicyResources.java b/core/java/android/app/admin/DevicePolicyResources.java
index 11b840fd4ad4..a49891356ccd 100644
--- a/core/java/android/app/admin/DevicePolicyResources.java
+++ b/core/java/android/app/admin/DevicePolicyResources.java
@@ -1836,6 +1836,27 @@ public final class DevicePolicyResources {
              */
             public static final String WORK_PROFILE_BADGED_LABEL =
                     PREFIX + "WORK_PROFILE_BADGED_LABEL";
+
+            /**
+             * Notification title. This notification lets the user know that they will be unable to
+             * receive phone calls or texts until the work profile is turned on.
+             */
+            public static final String WORK_PROFILE_TELEPHONY_PAUSED_TITLE =
+                    PREFIX + "WORK_PROFILE_TELEPHONY_UNAVAILABLE_TITLE";
+
+            /**
+             * Notification text. This notification lets the user know that they will be unable to
+             * receive phone calls or texts until the work profile is turned on.
+             */
+            public static final String WORK_PROFILE_TELEPHONY_PAUSED_BODY =
+                    PREFIX + "WORK_PROFILE_TELEPHONY_UNAVAILABLE_BODY";
+
+            /**
+             * Label for notification button. This button lets the user turn the work profile on.
+             */
+            public static final String WORK_PROFILE_TELEPHONY_PAUSED_TURN_ON_BUTTON =
+                    PREFIX + "TURN_ON_WORK_PROFILE_BUTTON_TEXT";
+
         }
 
         /**
diff --git a/core/res/res/drawable/ic_phone_disabled.xml b/core/res/res/drawable/ic_phone_disabled.xml
new file mode 100644
index 000000000000..6e6091242cc3
--- /dev/null
+++ b/core/res/res/drawable/ic_phone_disabled.xml
@@ -0,0 +1,25 @@
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="48"
+        android:viewportHeight="48"
+        android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M40.65,45.15 L28.9,33.45Q23.55,37.8 18.5,39.925Q13.45,42.05 8.6,42.05Q7.45,42.05 6.7,41.4Q5.95,40.75 5.95,39.75V33Q5.95,32.2 6.45,31.6Q6.95,31 7.7,30.85L13.65,29.55Q14.3,29.4 14.95,29.625Q15.6,29.85 16.1,30.4L20.85,35.3Q22.3,34.6 23.9,33.45Q25.5,32.3 26.75,31.3L3.2,7.7L5.35,5.55L42.8,43.05ZM18.1,36.75 L14.15,32.6Q14.15,32.6 14.15,32.6Q14.15,32.6 14.15,32.6L9,33.65Q9,33.65 9,33.65Q9,33.65 9,33.65V39Q9,39 9,39Q9,39 9,39Q11.25,38.9 13.65,38.3Q16.05,37.7 18.1,36.75ZM33.15,29.15 L31,27Q32.05,25.75 33.125,24.175Q34.2,22.6 35.05,21.3L30.05,16.25Q29.65,15.85 29.5,15.275Q29.35,14.7 29.5,14L30.8,7.75Q30.95,6.95 31.475,6.475Q32,6 32.7,6H39.7Q40.65,6 41.325,6.65Q42,7.3 42,8.25Q42,13.35 39.6,18.975Q37.2,24.6 33.15,29.15ZM36.45,18.45Q37.75,15.45 38.35,13.2Q38.95,10.95 38.9,9Q38.9,9 38.9,9Q38.9,9 38.9,9H33.6Q33.6,9 33.6,9Q33.6,9 33.6,9L32.45,14.45Q32.45,14.45 32.45,14.45Q32.45,14.45 32.45,14.45ZM36.45,18.45Q36.45,18.45 36.45,18.45Q36.45,18.45 36.45,18.45Q36.45,18.45 36.45,18.45Q36.45,18.45 36.45,18.45Q36.45,18.45 36.45,18.45Q36.45,18.45 36.45,18.45Q36.45,18.45 36.45,18.45Q36.45,18.45 36.45,18.45ZM18.1,36.75Q18.1,36.75 18.1,36.75Q18.1,36.75 18.1,36.75Q18.1,36.75 18.1,36.75Q18.1,36.75 18.1,36.75Q18.1,36.75 18.1,36.75Q18.1,36.75 18.1,36.75Q18.1,36.75 18.1,36.75Q18.1,36.75 18.1,36.75Z"/>
+</vector>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index da44c2efe14e..519869c777f4 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -556,6 +556,16 @@
     <!-- Title for the button that turns work profile on. To be used in a notification
         [CHAR LIMIT=NONE] -->
     <string name="personal_apps_suspended_turn_profile_on">Turn on</string>
+    <!-- Notification title. This notification lets the user know that they will be unable to
+     receive phone calls or texts until the work profile is turned on. [CHAR LIMIT=40] -->
+    <string name="work_profile_telephony_paused_title">Calls and messages are off</string>
+    <!-- Notification text. This notification lets the user know that they will be unable to
+     receive phone calls or texts until the work profile is turned on. [CHAR LIMIT=NONE] -->
+    <string name="work_profile_telephony_paused_text">You have paused work apps.
+        You won\'t receive phone calls or text messages.</string>
+    <!-- Label for notification button. This button lets the user turn the work profile on.
+         [CHAR LIMIT=15]  -->
+    <string name="work_profile_telephony_paused_turn_on_button">Unpause work apps</string>
 
     <!-- Display name for any time a piece of data refers to the owner of the phone. For example, this could be used in place of the phone's phone number. -->
     <string name="me">Me</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 18084d83364d..8818e735b96c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1189,6 +1189,9 @@
   <java-symbol type="string" name="personal_apps_suspension_soon_text" />
   <java-symbol type="string" name="personal_apps_suspension_text" />
   <java-symbol type="string" name="personal_apps_suspended_turn_profile_on" />
+  <java-symbol type="string" name="work_profile_telephony_paused_title" />
+  <java-symbol type="string" name="work_profile_telephony_paused_text" />
+  <java-symbol type="string" name="work_profile_telephony_paused_turn_on_button" />
   <java-symbol type="string" name="notification_work_profile_content_description" />
   <java-symbol type="string" name="factory_reset_warning" />
   <java-symbol type="string" name="factory_reset_message" />
@@ -1409,6 +1412,7 @@
 
   <java-symbol type="drawable" name="btn_borderless_rect" />
   <java-symbol type="drawable" name="ic_phone" />
+  <java-symbol type="drawable" name="ic_phone_disabled" />
   <java-symbol type="drawable" name="ic_bt_headphones_a2dp" />
   <java-symbol type="drawable" name="ic_bt_headset_hfp" />
   <java-symbol type="drawable" name="ic_bt_hearing_aid" />
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 12e722601e25..c9e41c405da7 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -392,5 +392,11 @@ message SystemMessage {
     // Note: this is a base ID, multiple notifications will be posted for each
     // abusive apps, with notification ID based off this ID.
     NOTE_ABUSIVE_BG_APPS_BASE = 0xc1b2508; // 203105544
+
+    // Notify the user that dialer and sms functionality are unavailable whilst the apps are
+    // paused in the work profile.
+    // Package: android
+    NOTE_ALL_MANAGED_SUBSCRIPTIONS_AND_MANAGED_PROFILE_OFF = 1006;
+
   }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c67ffd5a0d71..66adc2ca8633 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -134,6 +134,9 @@ import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_
 import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_GENERIC_MESSAGE;
 import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE;
 import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_TELEPHONY_PAUSED_BODY;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_TELEPHONY_PAUSED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_TELEPHONY_PAUSED_TURN_ON_BUTTON;
 import static android.app.admin.ProvisioningException.ERROR_ADMIN_PACKAGE_INSTALLATION_FAILED;
 import static android.app.admin.ProvisioningException.ERROR_PRE_CONDITION_FAILED;
 import static android.app.admin.ProvisioningException.ERROR_PROFILE_CREATION_FAILED;
@@ -141,6 +144,8 @@ import static android.app.admin.ProvisioningException.ERROR_REMOVE_NON_REQUIRED_
 import static android.app.admin.ProvisioningException.ERROR_SETTING_PROFILE_OWNER_FAILED;
 import static android.app.admin.ProvisioningException.ERROR_SET_DEVICE_OWNER_FAILED;
 import static android.app.admin.ProvisioningException.ERROR_STARTING_PROFILE_FAILED;
+import static android.content.Intent.ACTION_MANAGED_PROFILE_AVAILABLE;
+import static android.content.Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.pm.PackageManager.GET_META_DATA;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
@@ -1118,6 +1123,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
             } else if (ACTION_TURN_PROFILE_ON_NOTIFICATION.equals(action)) {
                 Slogf.i(LOG_TAG, "requesting to turn on the profile: " + userHandle);
                 mUserManager.requestQuietModeEnabled(false, UserHandle.of(userHandle));
+            } else if (ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) {
+                notifyIfManagedSubscriptionsAreUnavailable(
+                        UserHandle.of(userHandle), /* managedProfileAvailable= */ false);
+            } else if (ACTION_MANAGED_PROFILE_AVAILABLE.equals(action)) {
+                notifyIfManagedSubscriptionsAreUnavailable(
+                        UserHandle.of(userHandle), /* managedProfileAvailable= */ true);
             }
         }
 
@@ -1960,7 +1971,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
         filter.addAction(Intent.ACTION_USER_STOPPED);
         filter.addAction(Intent.ACTION_USER_SWITCHED);
         filter.addAction(Intent.ACTION_USER_UNLOCKED);
-        filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
+        filter.addAction(ACTION_MANAGED_PROFILE_UNAVAILABLE);
+        filter.addAction(ACTION_MANAGED_PROFILE_AVAILABLE);
         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
         mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
         filter = new IntentFilter();
@@ -18380,6 +18392,83 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
         });
     }
 
+    private void notifyIfManagedSubscriptionsAreUnavailable(
+            UserHandle managedProfile, boolean managedProfileAvailable) {
+        if (!isManagedProfile(managedProfile.getIdentifier())) {
+            Slog.wtf(
+                    LOG_TAG,
+                    "Expected managed profile when notified of profile availability change.");
+        }
+        if (getManagedSubscriptionsPolicy().getPolicyType()
+                != ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS) {
+            // There may be a subscription in the personal profile, in which case calls and
+            // texts may still be available. No need to notify the user.
+            return;
+        }
+        if (managedProfileAvailable) {
+            // When quiet mode is switched off calls and texts then become available to the user,
+            // so no need to keep showing the notification.
+            mInjector
+                    .getNotificationManager()
+                    .cancel(SystemMessage.NOTE_ALL_MANAGED_SUBSCRIPTIONS_AND_MANAGED_PROFILE_OFF);
+            return;
+        }
+        final Intent intent = new Intent(ACTION_TURN_PROFILE_ON_NOTIFICATION);
+        intent.putExtra(Intent.EXTRA_USER_HANDLE, managedProfile.getIdentifier());
+        final PendingIntent pendingIntent =
+                mInjector.pendingIntentGetBroadcast(
+                        mContext,
+                        /* requestCode= */ 0,
+                        intent,
+                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+        final Notification.Action turnProfileOnButton =
+                new Notification.Action.Builder(
+                        /* icon= */ null, getUnpauseWorkAppsButtonText(), pendingIntent)
+                        .build();
+
+        final Bundle extras = new Bundle();
+        extras.putString(
+                Notification.EXTRA_SUBSTITUTE_APP_NAME, getWorkProfileContentDescription());
+        final Notification notification =
+                new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN)
+                        .setSmallIcon(R.drawable.ic_phone_disabled)
+                        .setContentTitle(getUnpauseWorkAppsForTelephonyTitle())
+                        .setContentText(getUnpauseWorkAppsForTelephonyText())
+                        .setStyle(new Notification.BigTextStyle().bigText(
+                                getUnpauseWorkAppsForTelephonyText()))
+                        .addAction(turnProfileOnButton)
+                        .addExtras(extras)
+                        .setOngoing(false)
+                        .setShowWhen(true)
+                        .setAutoCancel(true)
+                        .build();
+
+        mInjector
+                .getNotificationManager()
+                .notifyAsUser(
+                        /* tag= */ null,
+                        SystemMessage.NOTE_ALL_MANAGED_SUBSCRIPTIONS_AND_MANAGED_PROFILE_OFF,
+                        notification,
+                        UserHandle.of(getProfileParentId(managedProfile.getIdentifier())));
+    }
+
+    private String getUnpauseWorkAppsButtonText() {
+        return getUpdatableString(
+                WORK_PROFILE_TELEPHONY_PAUSED_TURN_ON_BUTTON,
+                R.string.work_profile_telephony_paused_turn_on_button);
+    }
+
+    private String getUnpauseWorkAppsForTelephonyTitle() {
+        return getUpdatableString(
+                WORK_PROFILE_TELEPHONY_PAUSED_TITLE, R.string.work_profile_telephony_paused_title);
+    }
+
+    private String getUnpauseWorkAppsForTelephonyText() {
+        return getUpdatableString(
+                WORK_PROFILE_TELEPHONY_PAUSED_BODY,
+                R.string.work_profile_telephony_paused_text);
+    }
+
     @GuardedBy("getLockObject()")
     private void updateProfileOffDeadlineNotificationLocked(
             int profileUserId, ActiveAdmin profileOwner, int notificationState) {
-- 
GitLab