• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.systemui.car.wm.taskview;
18 
19 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
20 
21 import static com.android.systemui.car.wm.CarSystemUIProxyImpl.ensureManageSystemUIPermission;
22 
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.app.ActivityManager;
26 import android.app.ActivityOptions;
27 import android.app.ActivityTaskManager;
28 import android.app.PendingIntent;
29 import android.car.app.CarActivityManager;
30 import android.car.app.CarTaskViewClient;
31 import android.car.app.CarTaskViewHost;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.graphics.Rect;
35 import android.os.Binder;
36 import android.os.Bundle;
37 import android.os.DeadSystemRuntimeException;
38 import android.util.Slog;
39 import android.util.SparseArray;
40 import android.view.InsetsSource;
41 import android.view.SurfaceControl;
42 import android.window.WindowContainerTransaction;
43 
44 import com.android.internal.annotations.Keep;
45 import com.android.systemui.car.wm.CarSystemUIProxyImpl;
46 import com.android.wm.shell.ShellTaskOrganizer;
47 import com.android.wm.shell.common.SyncTransactionQueue;
48 import com.android.wm.shell.taskview.TaskViewBase;
49 import com.android.wm.shell.taskview.TaskViewTaskController;
50 import com.android.wm.shell.taskview.TaskViewTransitions;
51 
52 /** Server side implementation for {@code RemoteCarTaskView}. */
53 public class RemoteCarTaskViewServerImpl implements TaskViewBase {
54     private static final String TAG = RemoteCarTaskViewServerImpl.class.getSimpleName();
55 
56     private final Context mContext;
57     private final CarTaskViewClient mCarTaskViewClient;
58     private final TaskViewTaskController mTaskViewTaskController;
59     private final CarSystemUIProxyImpl mCarSystemUIProxy;
60     private final Binder mInsetsOwner = new Binder();
61     private final SparseArray<Rect> mInsets = new SparseArray<>();
62     private final ShellTaskOrganizer mShellTaskOrganizer;
63     private final CarActivityManager mCarActivityManager;
64     private final TaskViewTransitions mTaskViewTransitions;
65 
66     private RootTaskMediator mRootTaskMediator;
67     private boolean mReleased;
68 
69     private final CarTaskViewHost mHostImpl = new CarTaskViewHost() {
70         @Override
71         public void release() {
72             ensureManageSystemUIPermission(mContext);
73             if (mReleased) {
74                 Slog.w(TAG, "TaskView server part already released");
75                 return;
76             }
77             mInsets.clear();
78             int taskIdToRemove = INVALID_TASK_ID;
79             if (mTaskViewTaskController.getTaskInfo() != null) {
80                 taskIdToRemove = mTaskViewTaskController.getTaskInfo().taskId;
81             }
82             mTaskViewTaskController.release();
83 
84             if (mRootTaskMediator != null) {
85                 mRootTaskMediator.release();
86             } else if (taskIdToRemove != INVALID_TASK_ID) {
87                 Slog.w(TAG, "Removing embedded task: " + taskIdToRemove);
88                 ActivityTaskManager.getInstance().removeTask(taskIdToRemove);
89             }
90             mCarSystemUIProxy.onCarTaskViewReleased(RemoteCarTaskViewServerImpl.this);
91             mReleased = true;
92         }
93 
94         @Override
95         public void notifySurfaceCreated(SurfaceControl control) {
96             ensureManageSystemUIPermission(mContext);
97             mTaskViewTaskController.surfaceCreated(control);
98         }
99 
100         @Override
101         public void setWindowBounds(Rect bounds) {
102             ensureManageSystemUIPermission(mContext);
103             mTaskViewTaskController.setWindowBounds(bounds);
104         }
105 
106         @Override
107         public void notifySurfaceDestroyed() {
108             ensureManageSystemUIPermission(mContext);
109             mTaskViewTaskController.surfaceDestroyed();
110         }
111 
112         @Override
113         public void startActivity(
114                 PendingIntent pendingIntent,
115                 Intent fillInIntent,
116                 Bundle options,
117                 Rect launchBounds) {
118             ensureManageSystemUIPermission(mContext);
119             ActivityOptions opt = ActivityOptions.fromBundle(options);
120             // Need this for the pending intent to work under BAL hardening.
121             opt.setPendingIntentBackgroundActivityLaunchAllowedByPermission(true);
122             mTaskViewTaskController.startActivity(
123                     pendingIntent,
124                     fillInIntent,
125                     opt,
126                     launchBounds);
127         }
128 
129         /**
130          * Creates the root task in this task view. Should be called only once for the lifetime of
131          * this task view.
132          */
133         @Override
134         // TODO(b/24087642): Remove @Keep once this method is promoted to SystemApi.
135         // @Keep is used to prevent the removal of this method by the compiler as it is a hidden api
136         // in the base class.
137         @Keep
138         public void createRootTask(int displayId) {
139             ensureManageSystemUIPermission(mContext);
140             if (mRootTaskMediator != null) {
141                 throw new IllegalStateException("Root task is already created for this task view.");
142             }
143             mRootTaskMediator = new RootTaskMediator(displayId, /* isLaunchRoot= */ false,
144                     false, false, false, mShellTaskOrganizer,
145                     mTaskViewTaskController, RemoteCarTaskViewServerImpl.this,
146                     mCarActivityManager, mTaskViewTransitions);
147         }
148 
149         /**
150          * Creates the launch root task in this task view. Should be called only once for the
151          * lifetime of this task view.
152          */
153         @Override
154         // TODO(b/24087642): Remove @Keep once this method is promoted to SystemApi.
155         // @Keep is used to prevent the removal of this method by the compiler as it is a hidden api
156         // in the base class.
157         @Keep
158         public void createLaunchRootTask(int displayId, boolean embedHomeTask,
159                 boolean embedRecentsTask, boolean embedAssistantTask) {
160             ensureManageSystemUIPermission(mContext);
161             if (mCarSystemUIProxy.isLaunchRootTaskPresent(displayId)) {
162                 throw new IllegalArgumentException("Cannot create more than 1 root task on the"
163                         + " display=" + displayId);
164             }
165 
166             // TODO(b/299535374): Remove setHideTaskWithSurface once the taskviews with launch root
167             //  tasks are moved to an always visible window (surface) in SystemUI.
168             mTaskViewTaskController.setHideTaskWithSurface(false);
169 
170             mRootTaskMediator = new RootTaskMediator(displayId, /* isLaunchRoot= */ true,
171                     embedHomeTask, embedRecentsTask, embedAssistantTask, mShellTaskOrganizer,
172                     mTaskViewTaskController, RemoteCarTaskViewServerImpl.this,
173                     mCarActivityManager, mTaskViewTransitions);
174         }
175 
176         @Override
177         public void showEmbeddedTask() {
178             ensureManageSystemUIPermission(mContext);
179             ActivityManager.RunningTaskInfo taskInfo = mTaskViewTaskController.getTaskInfo();
180             if (taskInfo == null) {
181                 return;
182             }
183             if (mTaskViewTaskController.isUsingShellTransitions() && mTaskViewTransitions != null) {
184                 mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController, /* visible= */
185                         true, /* reorder= */ true);
186                 return;
187             }
188 
189             WindowContainerTransaction wct = new WindowContainerTransaction();
190             // Clears the hidden flag to make it TopFocusedRootTask: b/228092608
191             wct.setHidden(taskInfo.token, /* hidden= */ false);
192             // Moves the embedded task to the top to make it resumed: b/225388469
193             wct.reorder(taskInfo.token, /* onTop= */ true);
194             mShellTaskOrganizer.applyTransaction(wct);
195         }
196 
197         @Override
198         // TODO(b/24087642): Remove @Keep once this method is promoted to SystemApi.
199         // @Keep is used to prevent the removal of this method by the compiler as it is a hidden api
200         // in the base class.
201         @Keep
202         public void setTaskVisibility(boolean visibility) {
203             ensureManageSystemUIPermission(mContext);
204             ActivityManager.RunningTaskInfo taskInfo = mTaskViewTaskController.getTaskInfo();
205             if (taskInfo == null) {
206                 return;
207             }
208             if (mTaskViewTaskController.isUsingShellTransitions()) {
209                 mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController, visibility);
210                 return;
211             }
212 
213             WindowContainerTransaction wct = new WindowContainerTransaction();
214             wct.setHidden(taskInfo.token, !visibility);
215             mShellTaskOrganizer.applyTransaction(wct);
216         }
217 
218         @Override
219         // TODO(b/24087642): Remove @Keep once this method is promoted to SystemApi.
220         // @Keep is used to prevent the removal of this method by the compiler as it is a hidden api
221         // in the base class.
222         @Keep
223         public void reorderTask(boolean onTop) {
224             ensureManageSystemUIPermission(mContext);
225             ActivityManager.RunningTaskInfo taskInfo = mTaskViewTaskController.getTaskInfo();
226             if (taskInfo == null) {
227                 return;
228             }
229 
230             if (mTaskViewTaskController.isUsingShellTransitions()) {
231                 mTaskViewTransitions.reorderTaskViewTask(mTaskViewTaskController, onTop);
232                 return;
233             }
234 
235             WindowContainerTransaction wct = new WindowContainerTransaction();
236             wct.reorder(taskInfo.token, onTop);
237             mShellTaskOrganizer.applyTransaction(wct);
238         }
239 
240         @Override
241         public void addInsets(int index, int type, @NonNull Rect frame) {
242             ensureManageSystemUIPermission(mContext);
243             mInsets.append(InsetsSource.createId(mInsetsOwner, index, type), frame);
244 
245             if (mTaskViewTaskController.getTaskInfo() == null) {
246                 // The insets will be applied later as part of onTaskAppeared.
247                 Slog.w(TAG, "Cannot apply insets as the task token is not present.");
248                 return;
249             }
250             WindowContainerTransaction wct = new WindowContainerTransaction();
251             wct.addInsetsSource(mTaskViewTaskController.getTaskInfo().token,
252                     mInsetsOwner, index, type, frame, /* boundingRects = */ null);
253             mShellTaskOrganizer.applyTransaction(wct);
254         }
255 
256         @Override
257         public void removeInsets(int index, int type) {
258             ensureManageSystemUIPermission(mContext);
259             if (mInsets.size() == 0) {
260                 Slog.w(TAG, "No insets set.");
261                 return;
262             }
263             int id = InsetsSource.createId(mInsetsOwner, index, type);
264             if (!mInsets.contains(id)) {
265                 Slog.w(TAG, "Insets type: " + type + " can't be removed as it was not "
266                         + "applied as part of the last addInsets()");
267                 return;
268             }
269             mInsets.remove(id);
270 
271             if (mTaskViewTaskController.getTaskInfo() == null) {
272                 Slog.w(TAG, "Cannot remove insets as the task token is not present.");
273                 return;
274             }
275             WindowContainerTransaction wct = new WindowContainerTransaction();
276             wct.removeInsetsSource(mTaskViewTaskController.getTaskInfo().token,
277                     mInsetsOwner, index, type);
278             mShellTaskOrganizer.applyTransaction(wct);
279         }
280     };
281 
RemoteCarTaskViewServerImpl( Context context, ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue, CarTaskViewClient carTaskViewClient, CarSystemUIProxyImpl carSystemUIProxy, TaskViewTransitions taskViewTransitions, CarActivityManager carActivityManager)282     public RemoteCarTaskViewServerImpl(
283             Context context,
284             ShellTaskOrganizer organizer,
285             SyncTransactionQueue syncQueue,
286             CarTaskViewClient carTaskViewClient,
287             CarSystemUIProxyImpl carSystemUIProxy,
288             TaskViewTransitions taskViewTransitions,
289             CarActivityManager carActivityManager) {
290         mContext = context;
291         mCarTaskViewClient = carTaskViewClient;
292         mCarSystemUIProxy = carSystemUIProxy;
293         mShellTaskOrganizer = organizer;
294         mCarActivityManager = carActivityManager;
295         mTaskViewTransitions = taskViewTransitions;
296 
297         mTaskViewTaskController =
298                 new TaskViewTaskController(context, organizer, taskViewTransitions, syncQueue);
299         mTaskViewTaskController.setTaskViewBase(this);
300     }
301 
getHostImpl()302     public CarTaskViewHost getHostImpl() {
303         return mHostImpl;
304     }
305 
306     /** Returns true if a taskview with a launch root task exists on given {@code display}. */
hasLaunchRootTaskOnDisplay(int display)307     public boolean hasLaunchRootTaskOnDisplay(int display) {
308         return mRootTaskMediator != null && mRootTaskMediator.isLaunchRoot()
309                 && mRootTaskMediator.getDisplayId() == display;
310     }
311 
312     @Override
getCurrentBoundsOnScreen()313     public Rect getCurrentBoundsOnScreen() {
314         try {
315             return mCarTaskViewClient.getCurrentBoundsOnScreen();
316         } catch (DeadSystemRuntimeException ex) {
317             Slog.w(TAG, "Failed to call getCurrentBoundsOnScreen() as TaskView client has "
318                     + "already died. Host part will be released shortly.");
319         }
320         return new Rect(0, 0, 0, 0); // If it reaches here, it means that
321         // the host side is already being released so it doesn't matter what is returned from here.
322     }
323 
324     @Override
toString()325     public String toString() {
326         ActivityManager.RunningTaskInfo taskInfo = mTaskViewTaskController.getTaskInfo();
327         return "RemoteCarTaskViewServerImpl {"
328                 + "insets=" + mInsets
329                 + ", taskId=" + (taskInfo == null ? "null" : taskInfo.taskId)
330                 + ", taskInfo=" + (taskInfo == null ? "null" : taskInfo)
331                 + ", rootTaskMediator=" + (mRootTaskMediator == null ? "null"
332                 : mRootTaskMediator.toString())
333                 + "}";
334     }
335 
336     @Override
setResizeBgColor(SurfaceControl.Transaction transaction, int color)337     public void setResizeBgColor(SurfaceControl.Transaction transaction, int color) {
338         try {
339             mCarTaskViewClient.setResizeBackgroundColor(transaction, color);
340         } catch (DeadSystemRuntimeException e) {
341             Slog.w(TAG, "Failed to call setResizeBackgroundColor() as TaskView client has "
342                     + "already died. Host part will be released shortly.");
343         }
344     }
345 
346     @Override
onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash)347     public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
348         applyAllInsets();
349         try {
350             mCarTaskViewClient.onTaskAppeared(taskInfo, leash);
351         } catch (DeadSystemRuntimeException e) {
352             Slog.w(TAG, "Failed to call onTaskAppeared() as TaskView client has already died, "
353                     + "already died. Host part will be released shortly.");
354         }
355     }
356 
357     @Override
onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo)358     public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
359         try {
360             mCarTaskViewClient.onTaskInfoChanged(taskInfo);
361         } catch (DeadSystemRuntimeException e) {
362             Slog.w(TAG, "Failed to call onTaskInfoChanged() as TaskView client has already died, "
363                     + "already died. Host part will be released shortly.");
364         }
365     }
366 
367     @Override
onTaskVanished(ActivityManager.RunningTaskInfo taskInfo)368     public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
369         try {
370             mCarTaskViewClient.onTaskVanished(taskInfo);
371         } catch (DeadSystemRuntimeException e) {
372             Slog.w(TAG, "Failed to call onTaskVanished() as TaskView client has already died, "
373                     + "already died. Host part will be released shortly.");
374         }
375     }
376 
applyAllInsets()377     private void applyAllInsets() {
378         if (mInsets.size() == 0) {
379             Slog.w(TAG, "Cannot apply null or empty insets");
380             return;
381         }
382         if (mTaskViewTaskController.getTaskInfo() == null) {
383             Slog.w(TAG, "Cannot apply insets as the task token is not present.");
384             return;
385         }
386         WindowContainerTransaction wct = new WindowContainerTransaction();
387         for (int i = 0; i < mInsets.size(); i++) {
388             final int id = mInsets.keyAt(i);
389             final Rect frame = mInsets.valueAt(i);
390             wct.addInsetsSource(mTaskViewTaskController.getTaskInfo().token,
391                     mInsetsOwner, InsetsSource.getIndex(id), InsetsSource.getType(id), frame,
392                     null /* boundingRects */);
393         }
394         mShellTaskOrganizer.applyTransaction(wct);
395     }
396 
397     /**
398      * Gets the task info of the running task.
399      */
400     @Nullable
getTaskInfo()401     ActivityManager.RunningTaskInfo getTaskInfo() {
402         if (mTaskViewTaskController == null) {
403             return null;
404         }
405         return mTaskViewTaskController.getTaskInfo();
406     }
407 }
408