• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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