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