Make incoming and ongoing call notification foreground and highest priority.

Use startForeground()/stopForeground() for posting notification instead of NotificationManager.notify()/cancel(). It makes incoming and ongoing notification foreground and not able to snooze. It also colorize ongoing call notification for Android O, since we already setColorized().

Use setPriority(Notification.PRIORITY_MAX) for pre-O, and NotificationManager.IMPORTANCE_MAX for O. It makes incoming and ongoing notification stay on the top.

Test: StatusBarNotifierTest*2
PiperOrigin-RevId: 170924163
Change-Id: Iab1415b0161a4623a1ccf52d3948f32b6f93eeb2
diff --git a/java/com/android/dialer/notification/NotificationChannelManager.java b/java/com/android/dialer/notification/NotificationChannelManager.java
index 790aac3..93caed5 100644
--- a/java/com/android/dialer/notification/NotificationChannelManager.java
+++ b/java/com/android/dialer/notification/NotificationChannelManager.java
@@ -133,7 +133,7 @@
         new NotificationChannel(
             NotificationChannelId.ONGOING_CALL,
             context.getText(R.string.notification_channel_ongoing_call),
-            NotificationManager.IMPORTANCE_DEFAULT);
+            NotificationManager.IMPORTANCE_MAX);
     channel.setShowBadge(false);
     channel.enableLights(false);
     channel.enableVibration(false);
diff --git a/java/com/android/incallui/NotificationBroadcastReceiver.java b/java/com/android/incallui/NotificationBroadcastReceiver.java
index f83f84d..52d01f5 100644
--- a/java/com/android/incallui/NotificationBroadcastReceiver.java
+++ b/java/com/android/incallui/NotificationBroadcastReceiver.java
@@ -72,19 +72,19 @@
 
     // TODO: Commands of this nature should exist in the CallList.
     if (action.equals(ACTION_ANSWER_VIDEO_INCOMING_CALL)) {
-      answerIncomingCall(context, VideoProfile.STATE_BIDIRECTIONAL);
+      answerIncomingCall(VideoProfile.STATE_BIDIRECTIONAL);
     } else if (action.equals(ACTION_ANSWER_VOICE_INCOMING_CALL)) {
-      answerIncomingCall(context, VideoProfile.STATE_AUDIO_ONLY);
+      answerIncomingCall(VideoProfile.STATE_AUDIO_ONLY);
     } else if (action.equals(ACTION_DECLINE_INCOMING_CALL)) {
       Logger.get(context)
           .logImpression(DialerImpression.Type.REJECT_INCOMING_CALL_FROM_NOTIFICATION);
-      declineIncomingCall(context);
+      declineIncomingCall();
     } else if (action.equals(ACTION_HANG_UP_ONGOING_CALL)) {
-      hangUpOngoingCall(context);
+      hangUpOngoingCall();
     } else if (action.equals(ACTION_ACCEPT_VIDEO_UPGRADE_REQUEST)) {
       acceptUpgradeRequest(context);
     } else if (action.equals(ACTION_DECLINE_VIDEO_UPGRADE_REQUEST)) {
-      declineUpgradeRequest(context);
+      declineUpgradeRequest();
     } else if (action.equals(ACTION_PULL_EXTERNAL_CALL)) {
       context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
       int notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
@@ -99,7 +99,7 @@
   private void acceptUpgradeRequest(Context context) {
     CallList callList = InCallPresenter.getInstance().getCallList();
     if (callList == null) {
-      StatusBarNotifier.clearAllCallNotifications(context);
+      StatusBarNotifier.clearAllCallNotifications();
       LogUtil.e("NotificationBroadcastReceiver.acceptUpgradeRequest", "call list is empty");
     } else {
       DialerCall call = callList.getVideoUpgradeRequestCall();
@@ -109,10 +109,10 @@
     }
   }
 
-  private void declineUpgradeRequest(Context context) {
+  private void declineUpgradeRequest() {
     CallList callList = InCallPresenter.getInstance().getCallList();
     if (callList == null) {
-      StatusBarNotifier.clearAllCallNotifications(context);
+      StatusBarNotifier.clearAllCallNotifications();
       LogUtil.e("NotificationBroadcastReceiver.declineUpgradeRequest", "call list is empty");
     } else {
       DialerCall call = callList.getVideoUpgradeRequestCall();
@@ -122,10 +122,10 @@
     }
   }
 
-  private void hangUpOngoingCall(Context context) {
+  private void hangUpOngoingCall() {
     CallList callList = InCallPresenter.getInstance().getCallList();
     if (callList == null) {
-      StatusBarNotifier.clearAllCallNotifications(context);
+      StatusBarNotifier.clearAllCallNotifications();
       LogUtil.e("NotificationBroadcastReceiver.hangUpOngoingCall", "call list is empty");
     } else {
       DialerCall call = callList.getOutgoingCall();
@@ -140,10 +140,10 @@
     }
   }
 
-  private void answerIncomingCall(Context context, int videoState) {
+  private void answerIncomingCall(int videoState) {
     CallList callList = InCallPresenter.getInstance().getCallList();
     if (callList == null) {
-      StatusBarNotifier.clearAllCallNotifications(context);
+      StatusBarNotifier.clearAllCallNotifications();
       LogUtil.e("NotificationBroadcastReceiver.answerIncomingCall", "call list is empty");
     } else {
       DialerCall call = callList.getIncomingCall();
@@ -155,10 +155,10 @@
     }
   }
 
-  private void declineIncomingCall(Context context) {
+  private void declineIncomingCall() {
     CallList callList = InCallPresenter.getInstance().getCallList();
     if (callList == null) {
-      StatusBarNotifier.clearAllCallNotifications(context);
+      StatusBarNotifier.clearAllCallNotifications();
       LogUtil.e("NotificationBroadcastReceiver.declineIncomingCall", "call list is empty");
     } else {
       DialerCall call = callList.getIncomingCall();
diff --git a/java/com/android/incallui/StatusBarNotifier.java b/java/com/android/incallui/StatusBarNotifier.java
index becfad4..86ad9ff 100644
--- a/java/com/android/incallui/StatusBarNotifier.java
+++ b/java/com/android/incallui/StatusBarNotifier.java
@@ -28,7 +28,6 @@
 import static com.android.incallui.NotificationBroadcastReceiver.ACTION_TURN_ON_SPEAKER;
 
 import android.Manifest;
-import android.app.ActivityManager;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -74,7 +73,6 @@
 import com.android.dialer.lettertile.LetterTileDrawable;
 import com.android.dialer.lettertile.LetterTileDrawable.ContactType;
 import com.android.dialer.multimedia.MultimediaData;
-import com.android.dialer.notification.DialerNotificationManager;
 import com.android.dialer.notification.NotificationChannelId;
 import com.android.dialer.oem.MotorolaUtils;
 import com.android.dialer.util.DrawableConverter;
@@ -86,12 +84,12 @@
 import com.android.incallui.call.CallList;
 import com.android.incallui.call.DialerCall;
 import com.android.incallui.call.DialerCallListener;
+import com.android.incallui.call.TelecomAdapter;
 import com.android.incallui.ringtone.DialerRingtoneManager;
 import com.android.incallui.ringtone.InCallTonePlayer;
 import com.android.incallui.ringtone.ToneGeneratorFactory;
 import com.android.incallui.videotech.utils.SessionModificationState;
 import java.util.List;
-import java.util.Locale;
 import java.util.Objects;
 
 /** This class adds Notifications to the status bar for the in-call experience. */
@@ -100,7 +98,6 @@
         EnrichedCallManager.StateChangedListener,
         AudioModeProvider.AudioModeListener {
 
-  private static final String NOTIFICATION_TAG = "STATUS_BAR_NOTIFIER";
   private static final int NOTIFICATION_ID = 1;
 
   // Notification types
@@ -146,12 +143,12 @@
    * Should only be called from a irrecoverable state where it is necessary to dismiss all
    * notifications.
    */
-  static void clearAllCallNotifications(Context context) {
+  static void clearAllCallNotifications() {
     LogUtil.e(
         "StatusBarNotifier.clearAllCallNotifications",
         "something terrible happened, clear all InCall notifications");
 
-    DialerNotificationManager.cancel(context, NOTIFICATION_TAG, NOTIFICATION_ID);
+    TelecomAdapter.getInstance().stopForegroundNotification();
   }
 
   private static int getWorkStringFromPersonalString(int resId) {
@@ -219,10 +216,9 @@
       setStatusBarCallListener(null);
     }
     if (mCurrentNotification != NOTIFICATION_NONE) {
-      LogUtil.i("StatusBarNotifier.cancelNotification", "cancel");
-      DialerNotificationManager.cancel(mContext, NOTIFICATION_TAG, NOTIFICATION_ID);
+      TelecomAdapter.getInstance().stopForegroundNotification();
+      mCurrentNotification = NOTIFICATION_NONE;
     }
-    mCurrentNotification = NOTIFICATION_NONE;
   }
 
   /**
@@ -390,7 +386,7 @@
               "Canceling old notification so this one can be noisy");
           // Moving from a non-interuptive notification (or none) to a noisy one. Cancel the old
           // notification (if there is one) so the fullScreenIntent or HUN will show
-          DialerNotificationManager.cancel(mContext, NOTIFICATION_TAG, NOTIFICATION_ID);
+          TelecomAdapter.getInstance().stopForegroundNotification();
         }
         break;
       case NOTIFICATION_INCOMING_CALL_QUIET:
@@ -404,6 +400,8 @@
           builder.setColorized(true);
           builder.setChannelId(NotificationChannelId.ONGOING_CALL);
         }
+        // This will be ignored on O+ and handled by the channel
+        builder.setPriority(Notification.PRIORITY_MAX);
         break;
       default:
         break;
@@ -414,8 +412,7 @@
     builder.setSmallIcon(iconResId);
     builder.setContentTitle(contentTitle);
     builder.setLargeIcon(largeIcon);
-    builder.setColor(
-        mContext.getResources().getColor(R.color.dialer_theme_color, mContext.getTheme()));
+    builder.setColor(InCallPresenter.getInstance().getThemeColorManager().getPrimaryColor());
 
     if (isVideoUpgradeRequest) {
       builder.setUsesChronometer(false);
@@ -451,22 +448,9 @@
         "StatusBarNotifier.buildAndSendNotification",
         "displaying notification for " + notificationType);
 
-    try {
-      DialerNotificationManager.notify(mContext, NOTIFICATION_TAG, NOTIFICATION_ID, notification);
-    } catch (RuntimeException e) {
-      // TODO(b/34744003): Move the memory stats into silent feedback PSD.
-      ActivityManager activityManager = mContext.getSystemService(ActivityManager.class);
-      ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
-      activityManager.getMemoryInfo(memoryInfo);
-      throw new RuntimeException(
-          String.format(
-              Locale.US,
-              "Error displaying notification with photo type: %d (low memory? %b, availMem: %d)",
-              contactInfo.photoType,
-              memoryInfo.lowMemory,
-              memoryInfo.availMem),
-          e);
-    }
+    // If a notification exists, this will only update it.
+    TelecomAdapter.getInstance().startForegroundNotification(NOTIFICATION_ID, notification);
+
     Trace.endSection();
     call.getLatencyReport().onNotificationShown();
     mCurrentNotification = notificationType;
diff --git a/java/com/android/incallui/call/TelecomAdapter.java b/java/com/android/incallui/call/TelecomAdapter.java
index 005278b..0c0bbd4 100644
--- a/java/com/android/incallui/call/TelecomAdapter.java
+++ b/java/com/android/incallui/call/TelecomAdapter.java
@@ -16,12 +16,14 @@
 
 package com.android.incallui.call;
 
+import android.app.Notification;
 import android.content.ActivityNotFoundException;
 import android.content.Intent;
 import android.os.Looper;
 import android.support.annotation.MainThread;
 import android.support.annotation.VisibleForTesting;
 import android.telecom.InCallService;
+import com.android.dialer.common.Assert;
 import com.android.dialer.common.LogUtil;
 import java.util.List;
 
@@ -167,4 +169,24 @@
     }
     return false;
   }
+
+  /**
+   * Start a foreground notification. Calling it multiple times with the same id only updates the
+   * existing notification. Whoever called this function are responsible for calling {@link
+   * #stopForegroundNotification()} to remove the notification.
+   */
+  public void startForegroundNotification(int id, Notification notification) {
+    Assert.isNotNull(
+        mInCallService, "No inCallService available for starting foreground notification");
+    mInCallService.startForeground(id, notification);
+  }
+
+  /**
+   * Stop a started foreground notification. This does not stop {@code mInCallService} from running.
+   */
+  public void stopForegroundNotification() {
+    Assert.isNotNull(
+        mInCallService, "No inCallService available for stopping foreground notification");
+    mInCallService.stopForeground(true /*removeNotification*/);
+  }
 }