Add a button to blocking activity to restart task.

- always launch blocking activity into its task (singleTask)
- blocking activity handles new intent with blocked task id
- add system API restartTask() to CarPMS and SAMS
- when root activity is DO, blocking activity displays restart button

Bug: 78662129
Bug: 79595101
Test: Verified in demo app.
Change-Id: Ib8cfd731001e4604990b65fe60f3f12a0b5ae12b
diff --git a/service/src/com/android/car/SystemActivityMonitoringService.java b/service/src/com/android/car/SystemActivityMonitoringService.java
index 24ce2e7..2df9b00 100644
--- a/service/src/com/android/car/SystemActivityMonitoringService.java
+++ b/service/src/com/android/car/SystemActivityMonitoringService.java
@@ -85,10 +85,6 @@
         void onActivityLaunch(TopTaskInfoContainer topTask);
     }
 
-    private static final boolean DBG = false;
-
-    private static final int NUM_MAX_TASK_TO_FETCH = 10;
-
     private final Context mContext;
     private final IActivityManager mAm;
     private final ProcessObserver mProcessObserver;
@@ -192,6 +188,57 @@
         return false;
     }
 
+    /**
+     * Attempts to restart a task.
+     *
+     * <p>Restarts a task by sending an empty intent with flag
+     * {@link Intent#FLAG_ACTIVITY_CLEAR_TASK} to its root activity. If the task does not exist,
+     * do nothing.
+     *
+     * @param taskId id of task to be restarted.
+     */
+    public void restartTask(int taskId) {
+        String rootActivityName = null;
+        int userId = 0;
+        try {
+            findRootActivityName:
+            for (StackInfo info : mAm.getAllStackInfos()) {
+                for (int i = 0; i < info.taskIds.length; i++) {
+                    if (info.taskIds[i] == taskId) {
+                        rootActivityName = info.taskNames[i];
+                        userId = info.userId;
+                        if (Log.isLoggable(CarLog.TAG_AM, Log.DEBUG)) {
+                            Log.d(CarLog.TAG_AM, "Root activity is " + rootActivityName);
+                            Log.d(CarLog.TAG_AM, "User id is " + userId);
+                        }
+                        // Break out of nested loop.
+                        break findRootActivityName;
+                    }
+                }
+            }
+        } catch (RemoteException e) {
+            Log.e(CarLog.TAG_AM, "Could not get stack info", e);
+            return;
+        }
+
+        if (rootActivityName == null) {
+            Log.e(CarLog.TAG_AM, "Could not find root activity with task id " + taskId);
+            return;
+        }
+
+        Intent rootActivityIntent = new Intent();
+        rootActivityIntent.setComponent(ComponentName.unflattenFromString(rootActivityName));
+        // Clear the task the root activity is running in and start it in a new task.
+        // Effectively restart root activity.
+        rootActivityIntent.addFlags(
+                Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        if (Log.isLoggable(CarLog.TAG_AM, Log.INFO)) {
+            Log.i(CarLog.TAG_AM, "restarting root activity with user id " + userId);
+        }
+        mContext.startActivityAsUser(rootActivityIntent, new UserHandle(userId));
+    }
+
     public void registerActivityLaunchListener(ActivityLaunchListener listener) {
         synchronized (this) {
             mActivityLaunchListener = listener;
@@ -238,7 +285,7 @@
                         (focusedStackId == stackId && focusedStackId != mFocusedStackId)) {
                     mTopTasks.put(stackId, newTopTaskInfo);
                     mTasksToDispatch.add(newTopTaskInfo);
-                    if (DBG) {
+                    if (Log.isLoggable(CarLog.TAG_AM, Log.INFO)) {
                         Log.i(CarLog.TAG_AM, "New top task: " + newTopTaskInfo);
                     }
                 }
@@ -247,7 +294,7 @@
         }
         if (listener != null) {
             for (TopTaskInfoContainer topTask : mTasksToDispatch) {
-                if (DBG) {
+                if (Log.isLoggable(CarLog.TAG_AM, Log.INFO)) {
                     Log.i(CarLog.TAG_AM, "activity launched:" + topTask.toString());
                 }
                 listener.onActivityLaunch(topTask);
@@ -310,11 +357,9 @@
      * block the current task with the provided new activity.
      */
     private void handleBlockActivity(TopTaskInfoContainer currentTask, Intent newActivityIntent) {
-        newActivityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
         mContext.startActivityAsUser(newActivityIntent,
                 new UserHandle(currentTask.stackInfo.userId));
-        // now make stack with new activity focused.
+        // Now make stack with new activity focused.
         findTaskAndGrantFocus(newActivityIntent.getComponent());
     }
 
@@ -347,7 +392,7 @@
     private class ProcessObserver extends IProcessObserver.Stub {
         @Override
         public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
-            if (DBG) {
+            if (Log.isLoggable(CarLog.TAG_AM, Log.INFO)) {
                 Log.i(CarLog.TAG_AM,
                         String.format("onForegroundActivitiesChanged uid %d pid %d fg %b",
                     uid, pid, foregroundActivities));
@@ -364,7 +409,7 @@
     private class TaskListener extends TaskStackListener {
         @Override
         public void onTaskStackChanged() {
-            if (DBG) {
+            if (Log.isLoggable(CarLog.TAG_AM, Log.INFO)) {
                 Log.i(CarLog.TAG_AM, "onTaskStackChanged");
             }
             mHandler.requestUpdatingTask();