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 20 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 21 22 23 import android.Manifest; 24 import android.annotation.MainThread; 25 import android.annotation.NonNull; 26 import android.annotation.RequiresPermission; 27 import android.annotation.SystemApi; 28 import android.annotation.UiContext; 29 import android.app.Activity; 30 import android.car.Car; 31 import android.car.builtin.util.Slogf; 32 import android.content.Context; 33 import android.os.RemoteException; 34 import android.os.UserManager; 35 import android.util.Log; 36 import android.util.Slog; 37 38 import java.util.ArrayList; 39 import java.util.Iterator; 40 import java.util.List; 41 import java.util.concurrent.Executor; 42 43 /** 44 * This class is used for creating task views & is created on a per activity basis. 45 * @hide 46 */ 47 @SystemApi 48 public final class CarTaskViewController { 49 private static final String TAG = CarTaskViewController.class.getSimpleName(); 50 static final boolean DBG = Slogf.isLoggable(TAG, Log.DEBUG); 51 52 private final ICarSystemUIProxy mService; 53 private final Context mHostContext; 54 private final CarTaskViewControllerHostLifecycle mLifecycle; 55 private final List<RemoteCarTaskView> mRemoteCarTaskViews = 56 new ArrayList<>(); 57 private final CarTaskViewInputInterceptor mTaskViewInputInterceptor; 58 private final ICarActivityService mCarActivityService; 59 60 private boolean mReleased = false; 61 62 /** 63 * @param service the binder interface to communicate with the car system UI. 64 * @hide 65 */ CarTaskViewController(@iContext Context hostContext, @NonNull CarTaskViewControllerHostLifecycle lifecycle, @NonNull ICarSystemUIProxy service, ICarActivityService carActivityService)66 CarTaskViewController(@UiContext Context hostContext, 67 @NonNull CarTaskViewControllerHostLifecycle lifecycle, 68 @NonNull ICarSystemUIProxy service, 69 ICarActivityService carActivityService) { 70 mHostContext = hostContext; 71 mService = service; 72 mLifecycle = lifecycle; 73 mCarActivityService = carActivityService; 74 mTaskViewInputInterceptor = new CarTaskViewInputInterceptor(hostContext, lifecycle, this); 75 } 76 77 /** 78 * Creates a new {@link ControlledRemoteCarTaskView}. 79 * 80 * @param callbackExecutor the executor to get the {@link ControlledRemoteCarTaskViewCallback} 81 * on. 82 * @param controlledRemoteCarTaskViewCallback the callback to monitor the 83 * {@link ControlledRemoteCarTaskView} related 84 * events. 85 */ 86 @RequiresPermission(allOf = {Manifest.permission.INJECT_EVENTS, 87 Manifest.permission.INTERNAL_SYSTEM_WINDOW}, conditional = true) 88 @MainThread createControlledRemoteCarTaskView( @onNull ControlledRemoteCarTaskViewConfig controlledRemoteCarTaskViewConfig, @NonNull Executor callbackExecutor, @NonNull ControlledRemoteCarTaskViewCallback controlledRemoteCarTaskViewCallback)89 public void createControlledRemoteCarTaskView( 90 @NonNull ControlledRemoteCarTaskViewConfig controlledRemoteCarTaskViewConfig, 91 @NonNull Executor callbackExecutor, 92 @NonNull ControlledRemoteCarTaskViewCallback controlledRemoteCarTaskViewCallback) { 93 if (mReleased) { 94 throw new IllegalStateException("CarTaskViewController is already released"); 95 } 96 ControlledRemoteCarTaskView taskViewClient = 97 new ControlledRemoteCarTaskView( 98 mHostContext, 99 controlledRemoteCarTaskViewConfig, 100 callbackExecutor, 101 controlledRemoteCarTaskViewCallback, 102 /* carTaskViewController= */ this, 103 mHostContext.getSystemService(UserManager.class)); 104 105 try { 106 ICarTaskViewHost host = mService.createControlledCarTaskView( 107 taskViewClient.mICarTaskViewClient); 108 taskViewClient.setRemoteHost(host); 109 mRemoteCarTaskViews.add(taskViewClient); 110 111 if (controlledRemoteCarTaskViewConfig.mShouldCaptureGestures 112 || controlledRemoteCarTaskViewConfig.mShouldCaptureLongPress) { 113 assertPermission(Manifest.permission.INJECT_EVENTS); 114 assertPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW); 115 mTaskViewInputInterceptor.init(); 116 } 117 } catch (RemoteException e) { 118 Slogf.e(TAG, "Unable to create task view.", e); 119 } 120 } 121 122 /** 123 * Creates a new {@link RemoteCarRootTaskView}. 124 * 125 * @param callbackExecutor the executor to get the {@link RemoteCarRootTaskViewCallback} on. 126 * @param remoteCarRootTaskViewCallback the callback to monitor the 127 * {@link RemoteCarRootTaskView} related events. 128 * @hide 129 */ 130 @RequiresPermission(Car.PERMISSION_CONTROL_CAR_APP_LAUNCH) 131 @MainThread createRemoteCarRootTaskView( @onNull RemoteCarRootTaskViewConfig remoteCarRootTaskViewConfig, @NonNull Executor callbackExecutor, @NonNull RemoteCarRootTaskViewCallback remoteCarRootTaskViewCallback)132 public void createRemoteCarRootTaskView( 133 @NonNull RemoteCarRootTaskViewConfig remoteCarRootTaskViewConfig, 134 @NonNull Executor callbackExecutor, 135 @NonNull RemoteCarRootTaskViewCallback remoteCarRootTaskViewCallback) { 136 assertPermission(Car.PERMISSION_CONTROL_CAR_APP_LAUNCH); 137 if (mReleased) { 138 throw new IllegalStateException("CarTaskViewController is already released"); 139 } 140 RemoteCarRootTaskView taskViewClient = 141 new RemoteCarRootTaskView( 142 mHostContext, 143 remoteCarRootTaskViewConfig, 144 callbackExecutor, 145 remoteCarRootTaskViewCallback, 146 /* carTaskViewController= */ this, 147 mCarActivityService 148 ); 149 150 try { 151 ICarTaskViewHost host = mService.createCarTaskView(taskViewClient.mICarTaskViewClient); 152 taskViewClient.setRemoteHost(host); 153 mRemoteCarTaskViews.add(taskViewClient); 154 } catch (RemoteException e) { 155 Slogf.e(TAG, "Unable to create root task view.", e); 156 } 157 } 158 159 /** 160 * Creates a new {@link RemoteCarDefaultRootTaskView}. 161 * 162 * @param callbackExecutor the executor to get the {@link RemoteCarDefaultRootTaskViewCallback} 163 * on. 164 * @param remoteCarDefaultRootTaskViewCallback the callback to monitor the 165 * {@link RemoteCarDefaultRootTaskView} related 166 * events. 167 * @hide 168 */ 169 @MainThread createRemoteCarDefaultRootTaskView( @onNull RemoteCarDefaultRootTaskViewConfig remoteCarDefaultRootTaskViewConfig, @NonNull Executor callbackExecutor, @NonNull RemoteCarDefaultRootTaskViewCallback remoteCarDefaultRootTaskViewCallback)170 public void createRemoteCarDefaultRootTaskView( 171 @NonNull RemoteCarDefaultRootTaskViewConfig remoteCarDefaultRootTaskViewConfig, 172 @NonNull Executor callbackExecutor, 173 @NonNull RemoteCarDefaultRootTaskViewCallback remoteCarDefaultRootTaskViewCallback) { 174 if (mReleased) { 175 throw new IllegalStateException("CarTaskViewController is already released"); 176 } 177 RemoteCarDefaultRootTaskView taskViewClient = 178 new RemoteCarDefaultRootTaskView( 179 mHostContext, 180 remoteCarDefaultRootTaskViewConfig, 181 callbackExecutor, 182 remoteCarDefaultRootTaskViewCallback, 183 /* carTaskViewController= */ this 184 ); 185 186 try { 187 ICarTaskViewHost host = mService.createCarTaskView( 188 taskViewClient.mICarTaskViewClient); 189 taskViewClient.setRemoteHost(host); 190 mRemoteCarTaskViews.add(taskViewClient); 191 } catch (RemoteException e) { 192 Slogf.e(TAG, "Unable to create default root task view.", e); 193 } 194 } 195 onRemoteCarTaskViewReleased(@onNull RemoteCarTaskView taskView)196 void onRemoteCarTaskViewReleased(@NonNull RemoteCarTaskView taskView) { 197 if (mReleased) { 198 Slog.w(TAG, "Failed to remove the taskView as the " 199 + "CarTaskViewController is already released"); 200 return; 201 } 202 if (!mRemoteCarTaskViews.contains(taskView)) { 203 Slog.w(TAG, "This taskView has already been removed"); 204 return; 205 } 206 mRemoteCarTaskViews.remove(taskView); 207 } 208 assertPermission(String permission)209 private void assertPermission(String permission) { 210 if (mHostContext.checkCallingOrSelfPermission(permission) 211 != PERMISSION_GRANTED) { 212 throw new SecurityException("requires " + permission); 213 } 214 } 215 216 /** 217 * Releases all the resources held by the taskviews associated with this controller. 218 * 219 * <p> Once {@link #release()} is called, the current instance of {@link CarTaskViewController} 220 * cannot be used further. A new instance should be requested using 221 * {@link CarActivityManager#getCarTaskViewController(Activity, Executor, 222 * CarTaskViewControllerCallback)}. 223 */ 224 @MainThread release()225 public void release() { 226 if (mReleased) { 227 Slogf.w(TAG, "CarTaskViewController is already released"); 228 return; 229 } 230 releaseTaskViews(); 231 mTaskViewInputInterceptor.release(); 232 mReleased = true; 233 } 234 235 @MainThread releaseTaskViews()236 void releaseTaskViews() { 237 Iterator<RemoteCarTaskView> iterator = mRemoteCarTaskViews.iterator(); 238 while (iterator.hasNext()) { 239 RemoteCarTaskView taskView = iterator.next(); 240 // Remove the task view here itself because release triggers removal again which can 241 // result in concurrent modification exception. 242 iterator.remove(); 243 taskView.release(); 244 } 245 } 246 247 /** 248 * Brings all the embedded tasks to the front. 249 */ 250 @MainThread showEmbeddedTasks()251 public void showEmbeddedTasks() { 252 if (mReleased) { 253 throw new IllegalStateException("CarTaskViewController is already released"); 254 } 255 for (int i = 0, length = mRemoteCarTaskViews.size(); i < length; i++) { 256 RemoteCarTaskView remoteCarTaskView = mRemoteCarTaskViews.get(i); 257 // TODO(b/267314188): Add a new method in ICarSystemUI to call 258 // showEmbeddedTask in a single WCT for multiple tasks. 259 remoteCarTaskView.showEmbeddedTask(); 260 } 261 } 262 263 /** 264 * Brings all the embedded controlled tasks to the front. 265 */ 266 @MainThread showEmbeddedControlledTasks()267 void showEmbeddedControlledTasks() { 268 if (mReleased) { 269 throw new IllegalStateException("CarTaskViewController is already released"); 270 } 271 for (int i = 0, length = mRemoteCarTaskViews.size(); i < length; i++) { 272 RemoteCarTaskView carTaskView = mRemoteCarTaskViews.get(i); 273 // TODO(b/267314188): Add a new method in ICarSystemUI to call 274 // showEmbeddedTask in a single WCT for multiple tasks. 275 if (carTaskView instanceof ControlledRemoteCarTaskView) { 276 carTaskView.showEmbeddedTask(); 277 } 278 } 279 } 280 isHostVisible()281 boolean isHostVisible() { 282 return mLifecycle.isVisible(); 283 } 284 getRemoteCarTaskViews()285 List<RemoteCarTaskView> getRemoteCarTaskViews() { 286 return mRemoteCarTaskViews; 287 } 288 } 289