Zen upgrade notification

Test: runtest --path /extra/master/frameworks/base/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
Change-Id: Ia31f89af74d9bcee40e050bd6cc13f1e6a9a15d9
Fixes: 73000503
Fixes: 72876890
Fixes: 73110052
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index fef6495..8ab8361 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12134,6 +12134,12 @@
          * @hide
          */
         public static final String SHOW_MUTE_IN_CRASH_DIALOG = "show_mute_in_crash_dialog";
+
+        /**
+         * If nonzero, will show the zen upgrade notification when the user toggles DND on/off.
+         * @hide
+         */
+        public static final String SHOW_ZEN_UPGRADE_NOTIFICATION = "show_zen_upgrade_notification";
     }
 
     /**
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index d66322c..171d4d9 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -94,7 +94,7 @@
     private static final boolean DEFAULT_ALLOW_SCREEN_OFF = true;
     private static final boolean DEFAULT_ALLOW_SCREEN_ON = true;
 
-    private static final int XML_VERSION = 2;
+    public static final int XML_VERSION = 3;
     public static final String ZEN_TAG = "zen";
     private static final String ZEN_ATT_VERSION = "version";
     private static final String ZEN_ATT_USER = "user";
@@ -145,6 +145,7 @@
     public int user = UserHandle.USER_SYSTEM;
     public boolean allowWhenScreenOff = DEFAULT_ALLOW_SCREEN_OFF;
     public boolean allowWhenScreenOn = DEFAULT_ALLOW_SCREEN_ON;
+    public int version;
 
     public ZenRule manualRule;
     public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>();
@@ -431,6 +432,7 @@
         String tag = parser.getName();
         if (!ZEN_TAG.equals(tag)) return null;
         final ZenModeConfig rt = new ZenModeConfig();
+        rt.version = safeInt(parser, ZEN_ATT_VERSION, XML_VERSION);
         rt.user = safeInt(parser, ZEN_ATT_USER, rt.user);
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
             tag = parser.getName();
diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java
index 4a181b2..44adbb2 100644
--- a/core/java/com/android/internal/notification/SystemNotificationChannels.java
+++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java
@@ -49,6 +49,7 @@
     public static String USB = "USB";
     public static String FOREGROUND_SERVICE = "FOREGROUND_SERVICE";
     public static String HEAVY_WEIGHT_APP = "HEAVY_WEIGHT_APP";
+    public static String SYSTEM_CHANGES = "SYSTEM_CHANGES";
 
     public static void createAll(Context context) {
         final NotificationManager nm = context.getSystemService(NotificationManager.class);
@@ -152,6 +153,11 @@
                 .build());
         channelsList.add(heavyWeightChannel);
 
+        NotificationChannel systemChanges = new NotificationChannel(SYSTEM_CHANGES,
+                context.getString(R.string.notification_channel_system_changes),
+                NotificationManager.IMPORTANCE_LOW);
+        channelsList.add(systemChanges);
+
         nm.createNotificationChannels(channelsList);
     }
 
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index f2b7ab2..52ee9e8 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -426,10 +426,11 @@
     optional SettingProto show_first_crash_dialog = 349 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto show_restart_in_crash_dialog = 351 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto show_mute_in_crash_dialog = 352 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingsProto show_zen_upgrade_notification = 354  [ (android.privacy).dest = DEST_AUTOMATIC ];
 
     // Please insert fields in the same order as in
     // frameworks/base/core/java/android/provider/Settings.java.
-    // Next tag = 354;
+    // Next tag = 355;
 }
 
 message SecureSettingsProto {
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ec81df7..0efb6f9 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4854,4 +4854,11 @@
 
     <!-- Notification action for editing a screenshot (drawing on it, cropping it, etc) -->
     <string name="screenshot_edit">Edit</string>
+
+    <!-- Title for the notification channel notifying user of settings system changes (i.e. Do Not Disturb has changed). [CHAR LIMIT=NONE] -->
+    <string name="notification_channel_system_changes">System changes</string>
+    <!-- Title of notification indicating do not disturb settings have changed when upgrading to P -->
+    <string name="zen_upgrade_notification_title">Do Not Disturb has changed</string>
+    <!-- Content of notification indicating users can tap on the notification to go to dnd behavior settings -->
+    <string name="zen_upgrade_notification_content">Tap to check your behavior settings for interruptions</string>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index af25398..cbe0c69 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3102,6 +3102,7 @@
   <java-symbol type="string" name="notification_channel_retail_mode" />
   <java-symbol type="string" name="notification_channel_usb" />
   <java-symbol type="string" name="notification_channel_heavy_weight_app" />
+  <java-symbol type="string" name="notification_channel_system_changes" />
   <java-symbol type="string" name="config_defaultAutofillService" />
   <java-symbol type="string" name="config_defaultTextClassifierService" />
 
@@ -3257,4 +3258,7 @@
 
   <!-- For Wear devices -->
   <java-symbol type="array" name="config_wearActivityModeRadios" />
+
+  <java-symbol type="string" name="zen_upgrade_notification_title" />
+  <java-symbol type="string" name="zen_upgrade_notification_content" />
 </resources>
diff --git a/core/res/res/xml/default_zen_mode_config.xml b/core/res/res/xml/default_zen_mode_config.xml
index a446088..7849a2a 100644
--- a/core/res/res/xml/default_zen_mode_config.xml
+++ b/core/res/res/xml/default_zen_mode_config.xml
@@ -18,7 +18,7 @@
 -->
 
 <!-- Default configuration for zen mode.  See android.service.notification.ZenModeConfig. -->
-<zen version="2">
+<zen version="3">
     <allow alarms="true" media_system_other="true" calls="false" messages="false" reminders="false"
            events="false" />
 </zen>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index a0b6297..c36026c 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -354,6 +354,7 @@
                     Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS,
                     Settings.Global.SHOW_RESTART_IN_CRASH_DIALOG,
                     Settings.Global.SHOW_TEMPERATURE_WARNING,
+                    Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION,
                     Settings.Global.SMART_SELECTION_UPDATE_CONTENT_URL,
                     Settings.Global.SMART_SELECTION_UPDATE_METADATA_URL,
                     Settings.Global.SMS_OUTGOING_CHECK_INTERVAL_MS,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 1551b8e..6cf5eef 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1123,6 +1123,9 @@
         dumpSetting(s, p,
                 Settings.Global.SHOW_MUTE_IN_CRASH_DIALOG,
                 GlobalSettingsProto.SHOW_MUTE_IN_CRASH_DIALOG);
+        dumpSetting(s, p,
+                Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION,
+                GlobalSettingsProto.SHOW_ZEN_UPGRADE_NOTIFICATION);
 
         // Please insert new settings using the same order as in Settings.Global.
     }
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 08fdb97..608970f 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -204,6 +204,10 @@
     // Package: android
     NOTE_USB_TETHER = 47;
 
+    // Inform that DND settings have changed on OS upgrade
+    // Package: android
+    NOTE_ZEN_UPGRADE = 48;
+
     // ADD_NEW_IDS_ABOVE_THIS_LINE
     // Legacy IDs with arbitrary values appear below
     // Legacy IDs existed as stable non-conflicting constants prior to the O release
@@ -253,6 +257,7 @@
 
     // Notify the user about public volume state changes..
     // Package: com.android.systemui
+
     NOTE_STORAGE_PUBLIC = 0x53505542;  // 1397773634
 
     // Notify the user about private volume state changes.
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 7e3b551..44b83d8 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -18,8 +18,10 @@
 
 import android.app.AppOpsManager;
 import android.app.AutomaticZenRule;
+import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.NotificationManager.Policy;
+import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -44,6 +46,7 @@
 import android.os.Process;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.service.notification.Condition;
 import android.service.notification.ConditionProviderService;
@@ -61,6 +64,8 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.internal.notification.SystemNotificationChannels;
 import com.android.server.LocalServices;
 
 import libcore.io.IoUtils;
@@ -89,6 +94,7 @@
     private final H mHandler;
     private final SettingsObserver mSettingsObserver;
     @VisibleForTesting protected final AppOpsManager mAppOps;
+    @VisibleForTesting protected final NotificationManager mNotificationManager;
     protected ZenModeConfig mDefaultConfig;
     private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
     private final ZenModeFiltering mFiltering;
@@ -112,12 +118,14 @@
 
     protected String mDefaultRuleEveryNightName;
     protected String mDefaultRuleEventsName;
+    @VisibleForTesting protected boolean mIsBootComplete;
 
     public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders) {
         mContext = context;
         mHandler = new H(looper);
         addCallback(mMetrics);
         mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+        mNotificationManager =  context.getSystemService(NotificationManager.class);
 
         mDefaultConfig = new ZenModeConfig();
         setDefaultZenRules(mContext);
@@ -197,6 +205,8 @@
         mHandler.postMetricsTimer();
         cleanUpZenRules();
         evaluateZenMode("onSystemReady", true);
+        mIsBootComplete = true;
+        showZenUpgradeNotification(mZenMode);
     }
 
     public void onUserSwitched(int user) {
@@ -612,6 +622,10 @@
             throws XmlPullParserException, IOException {
         final ZenModeConfig config = ZenModeConfig.readXml(parser);
         if (config != null) {
+            if (config.version < ZenModeConfig.XML_VERSION) {
+                Settings.Global.putInt(mContext.getContentResolver(),
+                        Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 1);
+            }
             if (forRestore) {
                 //TODO: http://b/22388012
                 if (config.user != UserHandle.USER_SYSTEM) {
@@ -755,8 +769,10 @@
         return Global.getInt(mContext.getContentResolver(), Global.ZEN_MODE, Global.ZEN_MODE_OFF);
     }
 
-    private void setZenModeSetting(int zen) {
+    @VisibleForTesting
+    protected void setZenModeSetting(int zen) {
         Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, zen);
+        showZenUpgradeNotification(zen);
     }
 
     private int getPreviousRingerModeSetting() {
@@ -1139,6 +1155,41 @@
         }
     }
 
+    private void showZenUpgradeNotification(int zen) {
+        final boolean showNotification = mIsBootComplete
+                && zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+                && Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 0) != 0;
+
+        if (showNotification) {
+            mNotificationManager.notify(TAG, SystemMessage.NOTE_ZEN_UPGRADE,
+                    createZenUpgradeNotification());
+            Settings.Global.putInt(mContext.getContentResolver(),
+                    Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 0);
+        }
+    }
+
+    @VisibleForTesting
+    protected Notification createZenUpgradeNotification() {
+        Intent intent = new Intent(Settings.ACTION_ZEN_MODE_PRIORITY_SETTINGS)
+                .setPackage("com.android.settings")
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        final Bundle extras = new Bundle();
+        extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
+                mContext.getResources().getString(R.string.global_action_settings));
+        return new Notification.Builder(mContext, SystemNotificationChannels.SYSTEM_CHANGES)
+                .setSmallIcon(R.drawable.ic_settings)
+                .setContentTitle(mContext.getResources().getString(
+                        R.string.zen_upgrade_notification_title))
+                .setContentText(mContext.getResources().getString(
+                        R.string.zen_upgrade_notification_content))
+                .setAutoCancel(true)
+                .setLocalOnly(true)
+                .addExtras(extras)
+                .setContentIntent(PendingIntent.getActivity(mContext, 0, intent, 0, null))
+                .build();
+    }
+
     private final class Metrics extends Callback {
         private static final String COUNTER_PREFIX = "dnd_mode_";
         private static final long MINIMUM_LOG_PERIOD_MS = 60 * 1000;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index c532a8a..6144c51 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -17,21 +17,32 @@
 package com.android.server.notification;
 
 import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertEquals;
 import static junit.framework.TestCase.assertTrue;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
+import android.app.NotificationManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
 import android.media.AudioAttributes;
 import android.provider.Settings;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.server.UiServiceTestCase;
 
 import org.junit.Before;
@@ -46,15 +57,24 @@
 public class ZenModeHelperTest extends UiServiceTestCase {
 
     @Mock ConditionProviders mConditionProviders;
+    @Mock NotificationManager mNotificationManager;
+    @Mock private Resources mResources;
     private TestableLooper mTestableLooper;
     private ZenModeHelper mZenModeHelperSpy;
+    private Context mContext;
+    private ContentResolver mContentResolver;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
         mTestableLooper = TestableLooper.get(this);
-        mZenModeHelperSpy = spy(new ZenModeHelper(getContext(), mTestableLooper.getLooper(),
+        mContext = spy(getContext());
+        mContentResolver = mContext.getContentResolver();
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mContext.getSystemService(NotificationManager.class)).thenReturn(mNotificationManager);
+
+        mZenModeHelperSpy = spy(new ZenModeHelper(mContext, mTestableLooper.getLooper(),
                 mConditionProviders));
     }
 
@@ -194,4 +214,31 @@
             verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(shouldMute, usage);
         }
     }
+
+    @Test
+    public void testZenUpgradeNotification() {
+        // shows zen upgrade notification if stored settings says to shows, boot is completed
+        // and we're setting zen mode on
+        Settings.Global.putInt(mContentResolver, Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 1);
+        mZenModeHelperSpy.mIsBootComplete = true;
+        mZenModeHelperSpy.setZenModeSetting(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+        verify(mZenModeHelperSpy, times(1)).createZenUpgradeNotification();
+        verify(mNotificationManager, times(1)).notify(eq(ZenModeHelper.TAG),
+                eq(SystemMessage.NOTE_ZEN_UPGRADE), any());
+        assertEquals(0, Settings.Global.getInt(mContentResolver,
+                Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION, -1));
+    }
+
+    @Test
+    public void testNoZenUpgradeNotification() {
+        // doesn't show upgrade notification if stored settings says don't show
+        Settings.Global.putInt(mContentResolver, Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 0);
+        mZenModeHelperSpy.mIsBootComplete = true;
+        mZenModeHelperSpy.setZenModeSetting(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+        verify(mZenModeHelperSpy, never()).createZenUpgradeNotification();
+        verify(mNotificationManager, never()).notify(eq(ZenModeHelper.TAG),
+                eq(SystemMessage.NOTE_ZEN_UPGRADE), any());
+    }
 }