• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.quickstep.util;
18 
19 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
20 
21 import android.app.Activity;
22 
23 import androidx.annotation.NonNull;
24 
25 import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter;
26 import com.android.quickstep.RecentsModel;
27 
28 /**
29  * This class tracks the failure of a task launch through the TaskView.launchTask() call, in an
30  * edge case in which starting a new task may initially succeed (startActivity returns true), but
31  * the launch ultimately fails if the activity finishes while it is resuming.
32  *
33  * There are two signals this class checks, the launcher lifecycle and the transition completion.
34  * If we hit either of those signals and the task is no longer valid, then the registered failure
35  * callback will be notified.
36  */
37 public class TaskRemovedDuringLaunchListener implements ActivityLifecycleCallbacksAdapter {
38 
39     private Activity mActivity;
40     private int mLaunchedTaskId = INVALID_TASK_ID;
41     private Runnable mTaskLaunchFailedCallback = null;
42 
43     /**
44      * Registers a failure listener callback if it detects a scenario in which an app launch
45      * failed before the transition finished.
46      */
register(Activity activity, int launchedTaskId, @NonNull Runnable taskLaunchFailedCallback)47     public void register(Activity activity, int launchedTaskId,
48             @NonNull Runnable taskLaunchFailedCallback) {
49         activity.registerActivityLifecycleCallbacks(this);
50         mActivity = activity;
51         mLaunchedTaskId = launchedTaskId;
52         mTaskLaunchFailedCallback = taskLaunchFailedCallback;
53     }
54 
55     /**
56      * Unregisters the failure listener.
57      */
unregister()58     private void unregister() {
59         mActivity.unregisterActivityLifecycleCallbacks(this);
60         mActivity = null;
61         mLaunchedTaskId = INVALID_TASK_ID;
62         mTaskLaunchFailedCallback = null;
63     }
64 
65     /**
66      * Called when the transition finishes.
67      */
onTransitionFinished()68     public void onTransitionFinished() {
69         // The transition finished and Launcher was not stopped, check if the launch failed
70         checkTaskLaunchFailed();
71     }
72 
73     @Override
onActivityStopped(Activity activity)74     public void onActivityStopped(Activity activity) {
75         // The normal task launch case, Launcher stops and updates its state correctly
76         unregister();
77     }
78 
79     @Override
onActivityResumed(Activity activity)80     public void onActivityResumed(Activity activity) {
81         // The transition hasn't finished but Launcher was resumed, check if the launch failed
82         checkTaskLaunchFailed();
83     }
84 
85     @Override
onActivityDestroyed(Activity activity)86     public void onActivityDestroyed(Activity activity) {
87         // If we somehow don't get any of the above signals, then just unregister this listener
88         unregister();
89     }
90 
checkTaskLaunchFailed()91     private void checkTaskLaunchFailed() {
92         if (mLaunchedTaskId != INVALID_TASK_ID) {
93             final int launchedTaskId = mLaunchedTaskId;
94             final Runnable taskLaunchFailedCallback = mTaskLaunchFailedCallback;
95             RecentsModel.INSTANCE.getNoCreate().isTaskRemoved(mLaunchedTaskId, (taskRemoved) -> {
96                 if (taskRemoved) {
97                     ActiveGestureLog.INSTANCE.addLog("Launch failed, task (id=" + launchedTaskId
98                             + ") finished mid transition");
99                     taskLaunchFailedCallback.run();
100                 }
101             }, (task) -> true /* filter */);
102             unregister();
103         }
104     }
105 }
106