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