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