diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl index 06288c0794b07..37bdda0b03936 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -204,4 +204,12 @@ oneway interface ITaskStackListener { * @param frozen if true, Recents Tasks list is currently frozen, false otherwise */ void onRecentTaskListFrozenChanged(boolean frozen); + + /** + * Called when a task gets or loses focus. + * + * @param taskId id of the task. + * @param {@code true} if the task got focus, {@code false} if it lost it. + */ + void onTaskFocusChanged(int taskId, boolean focused); } diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index 343b3869d3546..da0aadb3ea481 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -199,4 +199,8 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { @Override public void onRecentTaskListFrozenChanged(boolean frozen) { } + + @Override + public void onTaskFocusChanged(int taskId, boolean focused) { + } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 5f3e3a39490c1..41d4ff785f049 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -5554,11 +5554,20 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (mLastResumedActivity != null && r.mUserId != mLastResumedActivity.mUserId) { mAmInternal.sendForegroundProfileChanged(r.mUserId); } + final Task prevTask = mLastResumedActivity != null ? mLastResumedActivity.getTask() : null; + updateResumedAppTrace(r); mLastResumedActivity = r; r.getDisplay().setFocusedApp(r, true); + if (prevTask == null || task != prevTask) { + if (prevTask != null) { + mTaskChangeNotificationController.notifyTaskFocusChanged(prevTask.mTaskId, false); + } + mTaskChangeNotificationController.notifyTaskFocusChanged(task.mTaskId, true); + } + applyUpdateLockStateLocked(r); applyUpdateVrModeLocked(r); diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java index 2dde0bac74a86..f715d8f3529b0 100644 --- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java @@ -59,6 +59,7 @@ class TaskChangeNotificationController { private static final int NOTIFY_TASK_LIST_UPDATED_LISTENERS_MSG = 24; private static final int NOTIFY_SINGLE_TASK_DISPLAY_EMPTY = 25; private static final int NOTIFY_TASK_LIST_FROZEN_UNFROZEN_MSG = 26; + private static final int NOTIFY_TASK_FOCUS_CHANGED_MSG = 27; // Delay in notifying task stack change listeners (in millis) private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100; @@ -179,6 +180,10 @@ class TaskChangeNotificationController { l.onRecentTaskListFrozenChanged(m.arg1 != 0); }; + private final TaskStackConsumer mNotifyTaskFocusChanged = (l, m) -> { + l.onTaskFocusChanged(m.arg1, m.arg2 != 0); + }; + @FunctionalInterface public interface TaskStackConsumer { void accept(ITaskStackListener t, Message m) throws RemoteException; @@ -273,6 +278,9 @@ class TaskChangeNotificationController { case NOTIFY_TASK_LIST_FROZEN_UNFROZEN_MSG: forAllRemoteListeners(mNotifyTaskListFrozen, msg); break; + case NOTIFY_TASK_FOCUS_CHANGED_MSG: + forAllRemoteListeners(mNotifyTaskFocusChanged, msg); + break; } } } @@ -565,4 +573,12 @@ class TaskChangeNotificationController { forAllLocalListeners(mNotifyTaskListFrozen, msg); msg.sendToTarget(); } + + /** @see ITaskStackListener#onTaskFocusChanged(int, boolean) */ + void notifyTaskFocusChanged(int taskId, boolean focused) { + final Message msg = mHandler.obtainMessage(NOTIFY_TASK_FOCUS_CHANGED_MSG, + taskId, focused ? 1 : 0); + forAllLocalListeners(mNotifyTaskFocusChanged, msg); + msg.sendToTarget(); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java index 9647178e6f9bf..e8c0362c9f324 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java @@ -144,4 +144,33 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase { anyInt() /* reason */, anyString() /* packageName */); verify(taskChangeNotifier, never()).notifyActivityDismissingDockedStack(); } + + /** + * Ensures that notify focus task changes. + */ + @Test + public void testNotifyTaskFocusChanged() { + final ActivityRecord fullScreenActivityA = new ActivityBuilder(mService).setCreateTask(true) + .setStack(mFullscreenStack).build(); + final Task taskA = fullScreenActivityA.getTask(); + + final TaskChangeNotificationController taskChangeNotifier = + mService.getTaskChangeNotificationController(); + spyOn(taskChangeNotifier); + + mService.setResumedActivityUncheckLocked(fullScreenActivityA, "resumeA"); + verify(taskChangeNotifier).notifyTaskFocusChanged(eq(taskA.mTaskId) /* taskId */, + eq(true) /* focused */); + reset(taskChangeNotifier); + + final ActivityRecord fullScreenActivityB = new ActivityBuilder(mService).setCreateTask(true) + .setStack(mFullscreenStack).build(); + final Task taskB = fullScreenActivityB.getTask(); + + mService.setResumedActivityUncheckLocked(fullScreenActivityB, "resumeB"); + verify(taskChangeNotifier).notifyTaskFocusChanged(eq(taskA.mTaskId) /* taskId */, + eq(false) /* focused */); + verify(taskChangeNotifier).notifyTaskFocusChanged(eq(taskB.mTaskId) /* taskId */, + eq(true) /* focused */); + } }