1 /* 2 * Copyright (C) 2022 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.car.carlauncher; 18 19 import static com.android.car.carlauncher.TaskViewManager.DBG; 20 21 import android.annotation.Nullable; 22 import android.app.Activity; 23 import android.app.ActivityManager; 24 import android.app.ActivityOptions; 25 import android.app.PendingIntent; 26 import android.content.Intent; 27 import android.graphics.Rect; 28 import android.os.UserManager; 29 import android.util.Log; 30 import android.view.Display; 31 import android.view.SurfaceControl; 32 import android.window.WindowContainerTransaction; 33 34 import com.android.wm.shell.ShellTaskOrganizer; 35 import com.android.wm.shell.common.SyncTransactionQueue; 36 import com.android.wm.shell.taskview.TaskViewTransitions; 37 38 import java.util.Set; 39 import java.util.concurrent.Executor; 40 41 /** 42 * A controlled {@link CarTaskView} is fully managed by the {@link TaskViewManager}. 43 * The underlying task will be restarted if it is crashed. 44 * 45 * It should be used when: 46 * <ul> 47 * <li>The underlying task is meant to be started by the host and be there forever.</li> 48 * </ul> 49 */ 50 final class ControlledCarTaskView extends CarTaskView { 51 private static final String TAG = ControlledCarTaskView.class.getSimpleName(); 52 53 private final Executor mCallbackExecutor; 54 private final ControlledCarTaskViewCallbacks mCallbacks; 55 private final UserManager mUserManager; 56 private final TaskViewManager mTaskViewManager; 57 private final ControlledCarTaskViewConfig mConfig; 58 @Nullable private RunnerWithBackoff mStartActivityWithBackoff; 59 ControlledCarTaskView( Activity context, ShellTaskOrganizer organizer, TaskViewTransitions taskViewTransitions, SyncTransactionQueue syncQueue, Executor callbackExecutor, ControlledCarTaskViewConfig controlledCarTaskViewConfig, ControlledCarTaskViewCallbacks callbacks, UserManager userManager, TaskViewManager taskViewManager)60 ControlledCarTaskView( 61 Activity context, 62 ShellTaskOrganizer organizer, 63 TaskViewTransitions taskViewTransitions, 64 SyncTransactionQueue syncQueue, 65 Executor callbackExecutor, 66 ControlledCarTaskViewConfig controlledCarTaskViewConfig, 67 ControlledCarTaskViewCallbacks callbacks, 68 UserManager userManager, 69 TaskViewManager taskViewManager) { 70 super(context, organizer, taskViewTransitions, syncQueue, true); 71 mCallbackExecutor = callbackExecutor; 72 mConfig = controlledCarTaskViewConfig; 73 mCallbacks = callbacks; 74 mUserManager = userManager; 75 mTaskViewManager = taskViewManager; 76 77 mCallbackExecutor.execute(() -> mCallbacks.onTaskViewCreated(this)); 78 if (mConfig.mAutoRestartOnCrash) { 79 mStartActivityWithBackoff = new RunnerWithBackoff(this::startActivityInternal); 80 } 81 } 82 83 @Override onCarTaskViewInitialized()84 protected void onCarTaskViewInitialized() { 85 super.onCarTaskViewInitialized(); 86 startActivity(); 87 mCallbackExecutor.execute(() -> mCallbacks.onTaskViewReady()); 88 } 89 90 /** 91 * Starts the underlying activity. 92 */ startActivity()93 public void startActivity() { 94 if (mStartActivityWithBackoff == null) { 95 startActivityInternal(); 96 return; 97 } 98 mStartActivityWithBackoff.stop(); 99 mStartActivityWithBackoff.start(); 100 } 101 stopTheStartActivityBackoffIfExists()102 private void stopTheStartActivityBackoffIfExists() { 103 if (mStartActivityWithBackoff == null) { 104 if (DBG) { 105 Log.d(TAG, "mStartActivityWithBackoff is not present."); 106 } 107 return; 108 } 109 mStartActivityWithBackoff.stop(); 110 } 111 startActivityInternal()112 private void startActivityInternal() { 113 if (!mUserManager.isUserUnlocked()) { 114 if (DBG) Log.d(TAG, "Can't start activity due to user is isn't unlocked"); 115 return; 116 } 117 118 // Don't start activity when the display is off. This can happen when the taskview is not 119 // attached to a window. 120 if (getDisplay() == null) { 121 Log.w(TAG, "Can't start activity because display is not available in " 122 + "taskview yet."); 123 return; 124 } 125 // Don't start activity when the display is off for ActivityVisibilityTests. 126 if (getDisplay().getState() != Display.STATE_ON) { 127 Log.w(TAG, "Can't start activity due to the display is off"); 128 return; 129 } 130 131 ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 132 /* enterResId= */ 0, /* exitResId= */ 0); 133 Rect launchBounds = new Rect(); 134 getBoundsOnScreen(launchBounds); 135 if (DBG) { 136 Log.d(TAG, "Starting (" + mConfig.mActivityIntent.getComponent() + ") on " 137 + launchBounds); 138 } 139 Intent fillInIntent = null; 140 if ((mConfig.mActivityIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0) { 141 fillInIntent = new Intent().addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 142 } 143 startActivity( 144 PendingIntent.getActivity(mContext, /* requestCode= */ 0, 145 mConfig.mActivityIntent, 146 PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT), 147 fillInIntent, options, launchBounds); 148 } 149 150 /** Gets the config used to build this controlled car task view. */ getConfig()151 ControlledCarTaskViewConfig getConfig() { 152 return mConfig; 153 } 154 155 /** 156 * See {@link ControlledCarTaskViewCallbacks#getDependingPackageNames()}. 157 */ getDependingPackageNames()158 Set<String> getDependingPackageNames() { 159 return mCallbacks.getDependingPackageNames(); 160 } 161 162 @Override onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash)163 public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { 164 super.onTaskAppeared(taskInfo, leash); 165 // Stop the start activity backoff because a task has already appeared. 166 stopTheStartActivityBackoffIfExists(); 167 } 168 169 @Override onTaskVanished(ActivityManager.RunningTaskInfo taskInfo)170 public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { 171 super.onTaskVanished(taskInfo); 172 if (mConfig.mAutoRestartOnCrash && mTaskViewManager.isHostVisible()) { 173 // onTaskVanished can be called when the host is in the background. In this case 174 // embedded activity should not be started. 175 Log.i(TAG, "Restarting task " + taskInfo.baseActivity 176 + " in ControlledCarTaskView"); 177 startActivity(); 178 } 179 } 180 181 @Override showEmbeddedTask(WindowContainerTransaction wct)182 void showEmbeddedTask(WindowContainerTransaction wct) { 183 if (getTaskInfo() == null) { 184 if (DBG) { 185 Log.d(TAG, "Embedded task not available, starting it now."); 186 } 187 startActivity(); 188 return; 189 } 190 super.showEmbeddedTask(wct); 191 } 192 193 @Override release()194 public void release() { 195 super.release(); 196 stopTheStartActivityBackoffIfExists(); 197 } 198 } 199