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 android.car.app; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.app.ActivityManager; 22 import android.car.builtin.util.Slogf; 23 import android.car.builtin.view.ViewHelper; 24 import android.content.Context; 25 import android.graphics.Rect; 26 import android.os.Binder; 27 import android.view.SurfaceControl; 28 29 import com.android.internal.annotations.GuardedBy; 30 31 import java.util.concurrent.Executor; 32 33 /** 34 * A {@link RemoteCarDefaultRootTaskView} can act as a default app container. A default app 35 * container is the container where all apps open by default. 36 * 37 * Please use this with caution. If this task view is used inside an activity, that activity might 38 * always remain running as all the other activities will start appearing inside this launch root 39 * instead of appearing in the full screen. 40 * 41 * @hide 42 */ 43 public final class RemoteCarDefaultRootTaskView extends RemoteCarTaskView { 44 private static final String TAG = RemoteCarDefaultRootTaskView.class.getSimpleName(); 45 46 private final Executor mCallbackExecutor; 47 private final RemoteCarDefaultRootTaskViewCallback mCallback; 48 private final CarTaskViewController mCarTaskViewController; 49 private final RemoteCarDefaultRootTaskViewConfig mConfig; 50 private final Rect mTmpRect = new Rect(); 51 private final Object mLock = new Object(); 52 @GuardedBy("mLock") 53 private final RootTaskStackManager mRootTaskStackManager = new RootTaskStackManager(); 54 55 @GuardedBy("mLock") 56 private ActivityManager.RunningTaskInfo mRootTask; 57 58 final ICarTaskViewClient mICarTaskViewClient = new ICarTaskViewClient.Stub() { 59 @Override 60 public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { 61 synchronized (mLock) { 62 if (mRootTask == null) { 63 mRootTask = taskInfo; 64 // If onTaskAppeared() is called, it implicitly means that super.isInitialized() 65 // is true, as the root task is created only after initialization. 66 long identity = Binder.clearCallingIdentity(); 67 try { 68 mCallbackExecutor.execute(() -> { 69 // Check for isReleased() because the car task view might have 70 // already been released but this code path is executed later because 71 // the executor was busy. 72 if (isReleased()) { 73 Slogf.w(TAG, "car task view has already been released"); 74 return; 75 } 76 mCallback.onTaskViewInitialized(); 77 }); 78 } finally { 79 Binder.restoreCallingIdentity(identity); 80 } 81 82 if (taskInfo.taskDescription != null) { 83 ViewHelper.seResizeBackgroundColor( 84 RemoteCarDefaultRootTaskView.this, 85 taskInfo.taskDescription.getBackgroundColor()); 86 } 87 updateWindowBounds(); 88 } 89 mRootTaskStackManager.taskAppeared(taskInfo, leash); 90 } 91 92 long identity = Binder.clearCallingIdentity(); 93 try { 94 mCallbackExecutor.execute(() -> { 95 if (isReleased()) { 96 Slogf.w(TAG, "car task view has already been released"); 97 return; 98 } 99 mCallback.onTaskAppeared(taskInfo); 100 }); 101 } finally { 102 Binder.restoreCallingIdentity(identity); 103 } 104 } 105 106 @Override 107 public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { 108 synchronized (mLock) { 109 if (mRootTask == null) { 110 return; 111 } 112 if (mRootTask.taskId == taskInfo.taskId && taskInfo.taskDescription != null) { 113 ViewHelper.seResizeBackgroundColor( 114 RemoteCarDefaultRootTaskView.this, 115 taskInfo.taskDescription.getBackgroundColor()); 116 } 117 mRootTaskStackManager.taskInfoChanged(taskInfo); 118 } 119 long identity = Binder.clearCallingIdentity(); 120 try { 121 mCallbackExecutor.execute(() -> { 122 if (isReleased()) { 123 Slogf.w(TAG, "car task view has already been released"); 124 return; 125 } 126 mCallback.onTaskInfoChanged(taskInfo); 127 }); 128 } finally { 129 Binder.restoreCallingIdentity(identity); 130 } 131 } 132 133 @Override 134 public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { 135 synchronized (mLock) { 136 if (mRootTask == null) { 137 return; 138 } 139 if (mRootTask.taskId == taskInfo.taskId) { 140 mRootTask = null; 141 } 142 mRootTaskStackManager.taskVanished(taskInfo); 143 } 144 long identity = Binder.clearCallingIdentity(); 145 try { 146 mCallbackExecutor.execute(() -> { 147 if (isReleased()) { 148 Slogf.w(TAG, "car task view has already been released"); 149 return; 150 } 151 mCallback.onTaskVanished(taskInfo); 152 }); 153 } finally { 154 Binder.restoreCallingIdentity(identity); 155 } 156 } 157 158 @Override 159 public void setResizeBackgroundColor(SurfaceControl.Transaction t, int color) { 160 ViewHelper.seResizeBackgroundColor(RemoteCarDefaultRootTaskView.this, color); 161 } 162 163 @Override 164 public Rect getCurrentBoundsOnScreen() { 165 ViewHelper.getBoundsOnScreen(RemoteCarDefaultRootTaskView.this, mTmpRect); 166 return mTmpRect; 167 } 168 }; 169 RemoteCarDefaultRootTaskView( @onNull Context context, RemoteCarDefaultRootTaskViewConfig config, @NonNull Executor callbackExecutor, @NonNull RemoteCarDefaultRootTaskViewCallback callback, CarTaskViewController carTaskViewController)170 RemoteCarDefaultRootTaskView( 171 @NonNull Context context, 172 RemoteCarDefaultRootTaskViewConfig config, 173 @NonNull Executor callbackExecutor, 174 @NonNull RemoteCarDefaultRootTaskViewCallback callback, 175 CarTaskViewController carTaskViewController) { 176 super(context); 177 mConfig = config; 178 mCallbackExecutor = callbackExecutor; 179 mCallback = callback; 180 mCarTaskViewController = carTaskViewController; 181 182 mCallbackExecutor.execute(() -> mCallback.onTaskViewCreated(this)); 183 } 184 185 /** 186 * Returns the task info of the top task running in the root task embedded in this task view. 187 * 188 * @return task info object of the top task. 189 */ 190 @Nullable getTopTaskInfo()191 public ActivityManager.RunningTaskInfo getTopTaskInfo() { 192 synchronized (mLock) { 193 return mRootTaskStackManager.getTopTask(); 194 } 195 } 196 197 @Override onInitialized()198 void onInitialized() { 199 // A signal when the surface and host-side are ready. This task view initialization 200 // completes after root task has been created and set as launch root. 201 createLaunchRootTask(mConfig.getDisplayId(), mConfig.embedsHomeTask(), 202 mConfig.embedsRecentsTask(), mConfig.embedsAssistantTask()); 203 } 204 205 @Override isInitialized()206 public boolean isInitialized() { 207 synchronized (mLock) { 208 return super.isInitialized() && mRootTask != null; 209 } 210 } 211 212 @Override onReleased()213 void onReleased() { 214 mCallbackExecutor.execute(() -> mCallback.onTaskViewReleased()); 215 mCarTaskViewController.onRemoteCarTaskViewReleased(this); 216 } 217 218 @Nullable 219 @Override getTaskInfo()220 public ActivityManager.RunningTaskInfo getTaskInfo() { 221 synchronized (mLock) { 222 return mRootTask; 223 } 224 } 225 getConfig()226 RemoteCarDefaultRootTaskViewConfig getConfig() { 227 return mConfig; 228 } 229 230 @Override toString()231 public String toString() { 232 return toString(/* withBounds= */ false); 233 } 234 toString(boolean withBounds)235 String toString(boolean withBounds) { 236 if (withBounds) { 237 ViewHelper.getBoundsOnScreen(this, mTmpRect); 238 } 239 return TAG + " {\n" 240 + " config=" + mConfig + "\n" 241 + " rootTaskId=" + (getTaskInfo() == null ? "null" : getTaskInfo().taskId) + "\n" 242 + (withBounds ? (" boundsOnScreen=" + mTmpRect) : "") 243 + "}\n"; 244 245 } 246 } 247