• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car.carlauncher.taskstack;
18 
19 import android.app.ActivityManager;
20 import android.app.ActivityTaskManager;
21 import android.app.TaskStackListener;
22 import android.content.ComponentName;
23 import android.os.Build;
24 import android.os.Handler;
25 import android.os.RemoteException;
26 import android.util.Log;
27 
28 import com.android.wm.shell.common.HandlerExecutor;
29 
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.concurrent.Executor;
33 
34 /** Organizer of many task stack listeners for the car launcher application. */
35 public class TaskStackChangeListeners {
36     private static final String TAG = TaskStackChangeListeners.class.getSimpleName();
37     private static final boolean DEBUG = Build.IS_DEBUGGABLE;
38     private static final TaskStackChangeListeners INSTANCE = new TaskStackChangeListeners(
39             new HandlerExecutor(Handler.getMain()));
40 
41     private final Impl mImpl;
42 
TaskStackChangeListeners(Executor executor)43     private TaskStackChangeListeners(Executor executor) {
44         mImpl = new Impl(executor);
45     }
46 
47     /** Returns a singleton instance of the {@link TaskStackChangeListeners}. */
getInstance()48     public static TaskStackChangeListeners getInstance() {
49         return INSTANCE;
50     }
51 
52     /**
53      * Registers a task stack listener with the system.
54      * This should be called on the main thread.
55      */
registerTaskStackListener(TaskStackListener listener)56     public void registerTaskStackListener(TaskStackListener listener) {
57         synchronized (mImpl) {
58             mImpl.addListener(listener);
59         }
60         if (DEBUG) {
61             Log.d(TAG, "registerTaskStackListener: " + listener);
62         }
63     }
64 
65     /**
66      * Unregisters a task stack listener with the system.
67      * This should be called on the main thread.
68      */
unregisterTaskStackListener(TaskStackListener listener)69     public void unregisterTaskStackListener(TaskStackListener listener) {
70         synchronized (mImpl) {
71             mImpl.removeListener(listener);
72         }
73         if (DEBUG) {
74             Log.d(TAG, "unregisterTaskStackListener: " + listener);
75         }
76     }
77 
78     private static class Impl extends TaskStackListener {
79 
80         private final List<TaskStackListener> mTaskStackListeners = new ArrayList<>();
81 
82         private final Executor mExecutor;
83         private boolean mRegistered;
84 
Impl(Executor executor)85         Impl(Executor executor) {
86             mExecutor = executor;
87         }
88 
addListener(TaskStackListener listener)89         public void addListener(TaskStackListener listener) {
90             synchronized (mTaskStackListeners) {
91                 mTaskStackListeners.add(listener);
92             }
93             if (!mRegistered) {
94                 // Register mTaskStackListener to IActivityManager only once if needed.
95                 try {
96                     ActivityTaskManager.getService().registerTaskStackListener(this);
97                     mRegistered = true;
98                 } catch (Exception e) {
99                     Log.w(TAG, "Failed to call registerTaskStackListener", e);
100                 }
101             }
102         }
103 
removeListener(TaskStackListener listener)104         public void removeListener(TaskStackListener listener) {
105             boolean isEmpty;
106             synchronized (mTaskStackListeners) {
107                 mTaskStackListeners.remove(listener);
108                 isEmpty = mTaskStackListeners.isEmpty();
109             }
110             if (isEmpty && mRegistered) {
111                 // Unregister mTaskStackListener once we have no more listeners
112                 try {
113                     ActivityTaskManager.getService().unregisterTaskStackListener(this);
114                     mRegistered = false;
115                 } catch (Exception e) {
116                     Log.w(TAG, "Failed to call unregisterTaskStackListener", e);
117                 }
118             }
119         }
120 
121         @Override
onTaskStackChanged()122         public void onTaskStackChanged() throws RemoteException {
123             mExecutor.execute(() -> {
124                 synchronized (mTaskStackListeners) {
125                     for (int i = 0; i < mTaskStackListeners.size(); i++) {
126                         try {
127                             mTaskStackListeners.get(i).onTaskStackChanged();
128                         } catch (RemoteException e) {
129                             Log.e(TAG, "onTaskStackChanged failed", e);
130                         }
131                     }
132                 }
133             });
134         }
135 
136         @Override
onTaskCreated(int taskId, ComponentName componentName)137         public void onTaskCreated(int taskId, ComponentName componentName) throws RemoteException {
138             mExecutor.execute(() -> {
139                 synchronized (mTaskStackListeners) {
140                     for (int i = 0; i < mTaskStackListeners.size(); i++) {
141                         try {
142                             mTaskStackListeners.get(i).onTaskCreated(taskId, componentName);
143                         } catch (RemoteException e) {
144                             Log.e(TAG, "onTaskCreated failed", e);
145                         }
146                     }
147                 }
148             });
149         }
150 
151         @Override
onTaskRemoved(int taskId)152         public void onTaskRemoved(int taskId) throws RemoteException {
153             mExecutor.execute(() -> {
154                 synchronized (mTaskStackListeners) {
155                     for (int i = 0; i < mTaskStackListeners.size(); i++) {
156                         try {
157                             mTaskStackListeners.get(i).onTaskRemoved(taskId);
158                         } catch (RemoteException e) {
159                             Log.e(TAG, "onTaskRemoved failed", e);
160                         }
161                     }
162                 }
163             });
164         }
165 
166         @Override
onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo)167         public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo)
168                 throws RemoteException {
169             mExecutor.execute(() -> {
170                 synchronized (mTaskStackListeners) {
171                     for (int i = 0; i < mTaskStackListeners.size(); i++) {
172                         try {
173                             mTaskStackListeners.get(i).onTaskMovedToFront(taskInfo);
174                         } catch (RemoteException e) {
175                             Log.e(TAG, "onTaskMovedToFront failed", e);
176                         }
177                     }
178                 }
179             });
180         }
181 
182         @Override
onTaskFocusChanged(int taskId, boolean focused)183         public void onTaskFocusChanged(int taskId, boolean focused) {
184             mExecutor.execute(() -> {
185                 synchronized (mTaskStackListeners) {
186                     for (int i = 0; i < mTaskStackListeners.size(); i++) {
187                         mTaskStackListeners.get(i).onTaskFocusChanged(taskId, focused);
188                     }
189                 }
190             });
191         }
192 
193         @Override
onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, boolean homeTaskVisible, boolean clearedTask, boolean wasVisible)194         public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
195                 boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
196             mExecutor.execute(() -> {
197                 synchronized (mTaskStackListeners) {
198                     for (TaskStackListener listener : mTaskStackListeners) {
199                         try {
200                             listener.onActivityRestartAttempt(
201                                     task, homeTaskVisible, clearedTask, wasVisible);
202                         } catch (RemoteException e) {
203                             Log.e(TAG, "onActivityRestartAttempt failed", e);
204                         }
205                     }
206                 }
207             });
208         }
209     }
210 }
211