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 package android.car.app; 17 18 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED; 19 20 import android.annotation.MainThread; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.app.Activity; 24 import android.car.app.CarTaskViewControllerHostLifecycle.CarTaskViewControllerHostLifecycleObserver; 25 import android.car.builtin.app.ActivityManagerHelper; 26 import android.car.builtin.util.Slogf; 27 import android.car.user.CarUserManager; 28 import android.car.user.CarUserManager.UserLifecycleListener; 29 import android.car.user.UserLifecycleEventFilter; 30 import android.content.Context; 31 import android.os.Binder; 32 import android.os.IBinder; 33 import android.os.RemoteException; 34 import android.util.ArrayMap; 35 36 import com.android.internal.annotations.GuardedBy; 37 38 import java.util.concurrent.Executor; 39 40 /** 41 * This class is responsible to create and manage the {@link CarTaskViewController} instances. 42 * - It connects to the {@link android.car.app.CarSystemUIProxy} & listens to the {@link Activity}'s 43 * lifecycle. 44 * - It is also responsible to dispatch {@link CarTaskViewControllerCallback} methods to the 45 * clients. 46 */ 47 final class CarTaskViewControllerSupervisor { 48 private static final String TAG = CarTaskViewControllerSupervisor.class.getSimpleName(); 49 private final ArrayMap<CarTaskViewControllerHostLifecycle, ActivityHolder> mActivityHolders = 50 new ArrayMap<>(); 51 private final ICarActivityService mCarActivityService; 52 private final Executor mMainExecutor; 53 54 @Nullable private ICarSystemUIProxyCallback mSystemUIProxyCallback = null; 55 @Nullable private ICarSystemUIProxy mICarSystemUI = null; 56 57 private final IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { 58 @Override 59 public void binderDied() { 60 long identity = Binder.clearCallingIdentity(); 61 try { 62 mMainExecutor.execute(() -> onSystemUIProxyDisconnected()); 63 } finally { 64 Binder.restoreCallingIdentity(identity); 65 } 66 } 67 }; 68 69 private final CarTaskViewControllerHostLifecycleObserver 70 mCarTaskViewControllerHostLifecycleObserver = 71 new CarTaskViewControllerHostLifecycleObserver() { 72 public void onHostAppeared(CarTaskViewControllerHostLifecycle lifecycle) { 73 mActivityHolders.get(lifecycle).maybeShowControlledTasks(); 74 } 75 76 @Override 77 public void onHostDisappeared(CarTaskViewControllerHostLifecycle lifecycle) { 78 } 79 80 @Override 81 public void onHostDestroyed(CarTaskViewControllerHostLifecycle lifecycle) { 82 lifecycle.unregisterObserver(this); 83 84 ActivityHolder activityHolder = mActivityHolders.remove(lifecycle); 85 activityHolder.onActivityDestroyed(); 86 87 // When all the underlying activities are destroyed, the callback should be 88 // removed from the CarActivityService as it's no longer required. 89 // A new callback will be registered when a new activity calls the 90 // createTaskViewController. 91 if (mActivityHolders.isEmpty()) { 92 try { 93 mCarActivityService.removeCarSystemUIProxyCallback( 94 mSystemUIProxyCallback); 95 mSystemUIProxyCallback = null; 96 } catch (RemoteException e) { 97 Slogf.e(TAG, "Failed to remove CarSystemUIProxyCallback", e); 98 } 99 } 100 } 101 }; 102 103 /** 104 * @param carActivityService the handle to the {@link com.android.car.am.CarActivityService}. 105 */ CarTaskViewControllerSupervisor(ICarActivityService carActivityService, Executor mainExecutor, @NonNull CarUserManager carUserManager)106 CarTaskViewControllerSupervisor(ICarActivityService carActivityService, Executor mainExecutor, 107 @NonNull CarUserManager carUserManager) { 108 mCarActivityService = carActivityService; 109 mMainExecutor = mainExecutor; 110 UserLifecycleEventFilter filter = new UserLifecycleEventFilter.Builder() 111 .addEventType(USER_LIFECYCLE_EVENT_TYPE_UNLOCKED).build(); 112 carUserManager.addListener(mainExecutor, filter, mUserLifecycleListener); 113 } 114 getToken(Activity activity)115 private static IBinder getToken(Activity activity) { 116 return ActivityManagerHelper.getActivityToken(activity); 117 } 118 119 /** 120 * Creates a new {@link CarTaskViewController} instance for the provided {@code hostActivity}. 121 * 122 * @param callbackExecutor the executor which the {@code carTaskViewControllerCallback} methods 123 * will be called upon. 124 * @param carTaskViewControllerCallback the life callback methods for the 125 * {@link CarTaskViewController}. 126 * @param hostActivity the activity which will be hosting the taskviews that will be created 127 * using the underlying {@link CarTaskViewController}. 128 * @throws RemoteException as thrown by 129 * {@link ICarSystemUIProxy#createCarTaskView(CarTaskViewClient)}. 130 */ 131 @MainThread createCarTaskViewController( Context context, @NonNull CarTaskViewControllerHostLifecycle hostActivity, @NonNull Executor callbackExecutor, @NonNull CarTaskViewControllerCallback carTaskViewControllerCallback)132 void createCarTaskViewController( 133 Context context, 134 @NonNull CarTaskViewControllerHostLifecycle hostActivity, 135 @NonNull Executor callbackExecutor, 136 @NonNull CarTaskViewControllerCallback carTaskViewControllerCallback) 137 throws RemoteException { 138 if (mActivityHolders.containsKey(hostActivity)) { 139 throw new IllegalArgumentException("A CarTaskViewController already exists for this " 140 + "activity. Cannot create another one."); 141 } 142 hostActivity.registerObserver(mCarTaskViewControllerHostLifecycleObserver); 143 ActivityHolder activityHolder = new ActivityHolder(context, hostActivity, callbackExecutor, 144 carTaskViewControllerCallback, mCarActivityService); 145 mActivityHolders.put(hostActivity, activityHolder); 146 147 if (mSystemUIProxyCallback != null && mICarSystemUI != null) { 148 // If there is already a connection with the CarSystemUIProxy, trigger onConnected 149 // right away. 150 activityHolder.onCarSystemUIConnected(mICarSystemUI); 151 return; 152 } 153 if (mSystemUIProxyCallback != null) { 154 // If there is no connection, but callback is registered, do nothing; as when 155 // connection is made, it will automatically trigger onConnected for all the activity 156 // holders. 157 Slogf.d(TAG, "SystemUIProxyCallback already registered but not connected yet."); 158 return; 159 } 160 161 // If the CarSystemUIProxyCallback is not registered, register it now. 162 mSystemUIProxyCallback = new ICarSystemUIProxyCallback.Stub() { 163 @Override 164 public void onConnected(ICarSystemUIProxy carSystemUIProxy) { 165 long identity = Binder.clearCallingIdentity(); 166 try { 167 mMainExecutor.execute(() -> onSystemUIProxyConnected(carSystemUIProxy)); 168 } finally { 169 Binder.restoreCallingIdentity(identity); 170 } 171 } 172 }; 173 try { 174 mCarActivityService.addCarSystemUIProxyCallback(mSystemUIProxyCallback); 175 } catch (RemoteException e) { 176 mSystemUIProxyCallback = null; 177 throw e; 178 } 179 } 180 181 @MainThread onSystemUIProxyConnected(ICarSystemUIProxy systemUIProxy)182 private void onSystemUIProxyConnected(ICarSystemUIProxy systemUIProxy) { 183 mICarSystemUI = systemUIProxy; 184 try { 185 systemUIProxy.asBinder().linkToDeath(mDeathRecipient, /* flags= */ 0); 186 } catch (RemoteException ex) { 187 throw new IllegalStateException("Linking to binder death failed for " 188 + "ICarSystemUIProxy, the System UI might already died", ex); 189 } 190 191 for (ActivityHolder activityHolder : mActivityHolders.values()) { 192 activityHolder.onCarSystemUIConnected(systemUIProxy); 193 } 194 } 195 196 @MainThread onSystemUIProxyDisconnected()197 private void onSystemUIProxyDisconnected() { 198 mICarSystemUI.asBinder().unlinkToDeath(mDeathRecipient, /* flags= */ 0); 199 mICarSystemUI = null; 200 201 for (ActivityHolder activityHolder : mActivityHolders.values()) { 202 activityHolder.onCarSystemUIDisconnected(); 203 } 204 // No need to remove the holders as activities are still active and will create the 205 // taskviews again, when system ui will be connected again. 206 } 207 208 private static final class ActivityHolder { 209 private final Context mContext; 210 private final CarTaskViewControllerHostLifecycle mActivity; 211 private final Executor mCallbackExecutor; 212 private final CarTaskViewControllerCallback mCarTaskViewControllerCallback; 213 private final ICarActivityService mCarActivityService; 214 private final Object mLock = new Object(); 215 216 @GuardedBy("mLock") 217 private CarTaskViewController mCarTaskViewController; 218 ActivityHolder(Context context, CarTaskViewControllerHostLifecycle activity, Executor callbackExecutor, CarTaskViewControllerCallback carTaskViewControllerCallback, ICarActivityService carActivityService)219 private ActivityHolder(Context context, 220 CarTaskViewControllerHostLifecycle activity, 221 Executor callbackExecutor, 222 CarTaskViewControllerCallback carTaskViewControllerCallback, 223 ICarActivityService carActivityService) { 224 mContext = context; 225 mActivity = activity; 226 mCallbackExecutor = callbackExecutor; 227 mCarTaskViewControllerCallback = carTaskViewControllerCallback; 228 mCarActivityService = carActivityService; 229 } 230 maybeShowControlledTasks()231 private void maybeShowControlledTasks() { 232 synchronized (mLock) { 233 if (mCarTaskViewController == null || !mCarTaskViewController.isHostVisible()) { 234 return; 235 } 236 mCarTaskViewController.showEmbeddedControlledTasks(); 237 } 238 } 239 onCarSystemUIConnected(ICarSystemUIProxy systemUIProxy)240 private void onCarSystemUIConnected(ICarSystemUIProxy systemUIProxy) { 241 synchronized (mLock) { 242 mCarTaskViewController = 243 new CarTaskViewController(mContext, mActivity, systemUIProxy, 244 mCarActivityService); 245 } 246 mCallbackExecutor.execute(() -> { 247 synchronized (mLock) { 248 // Check for null because the mCarTaskViewController might have already been 249 // released but this code path is executed later because the executor was 250 // busy. 251 if (mCarTaskViewController == null) { 252 Slogf.w(TAG, 253 "car task view controller not found when triggering callback, not" 254 + " dispatching onConnected"); 255 return; 256 } 257 mCarTaskViewControllerCallback.onConnected(mCarTaskViewController); 258 } 259 }); 260 } 261 onCarSystemUIDisconnected()262 private void onCarSystemUIDisconnected() { 263 synchronized (mLock) { 264 if (mCarTaskViewController == null) { 265 Slogf.w(TAG, 266 "car task view controller not found, not dispatching onDisconnected"); 267 return; 268 } 269 // Only release the taskviews and not the controller because the system ui might get 270 // connected while the activity is still visible. 271 mCarTaskViewController.releaseTaskViews(); 272 } 273 mCallbackExecutor.execute(() -> { 274 synchronized (mLock) { 275 if (mCarTaskViewController == null) { 276 Slogf.w(TAG, "car task view controller not found when triggering " 277 + "callback, not dispatching onDisconnected"); 278 return; 279 } 280 mCarTaskViewControllerCallback.onDisconnected(mCarTaskViewController); 281 } 282 }); 283 } 284 onActivityDestroyed()285 private void onActivityDestroyed() { 286 releaseController(); 287 } 288 releaseController()289 private void releaseController() { 290 synchronized (mLock) { 291 if (mCarTaskViewController == null) { 292 Slogf.w(TAG, "car task view controller not found, not releasing"); 293 return; 294 } 295 mCarTaskViewController.release(); 296 mCarTaskViewController = null; 297 } 298 } 299 } 300 301 private UserLifecycleListener mUserLifecycleListener = new UserLifecycleListener() { 302 @Override 303 public void onEvent(@NonNull CarUserManager.UserLifecycleEvent event) { 304 // Only called when USER_LIFECYCLE_EVENT_TYPE_UNLOCKED. 305 for (int i = mActivityHolders.size() - 1; i >= 0; --i) { 306 ActivityHolder activityHolder = mActivityHolders.valueAt(i); 307 activityHolder.maybeShowControlledTasks(); 308 } 309 } 310 }; 311 } 312