1 /*
2  * Copyright (C) 2016 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.server.wm;
18 
19 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_UNKNOWN_APP_VISIBILITY;
20 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
21 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
22 
23 import android.annotation.NonNull;
24 import android.util.ArrayMap;
25 import android.util.Slog;
26 
27 import com.android.server.wm.WindowManagerService.H;
28 
29 import java.io.PrintWriter;
30 
31 /**
32  * Manages the set of {@link AppWindowToken}s for which we don't know yet whether it's visible or
33  * not. This happens when starting an activity while the lockscreen is showing. In that case, the
34  * keyguard flags an app might set influence it's visibility, so we wait until this is resolved to
35  * start the transition to avoid flickers.
36  */
37 class UnknownAppVisibilityController {
38 
39     private static final String TAG = TAG_WITH_CLASS_NAME ? "UnknownAppVisibility" : TAG_WM;
40 
41     /**
42      * We are currently waiting until the app is done resuming.
43      */
44     private static final int UNKNOWN_STATE_WAITING_RESUME = 1;
45 
46     /**
47      * The activity has finished resuming, and we are waiting on the next relayout.
48      */
49     private static final int UNKNOWN_STATE_WAITING_RELAYOUT = 2;
50 
51     /**
52      * The client called {@link Session#relayout} with the appropriate Keyguard flags and we are
53      * waiting until activity manager has updated the visibilities of all the apps.
54      */
55     private static final int UNKNOWN_STATE_WAITING_VISIBILITY_UPDATE = 3;
56 
57     // Set of apps for which we don't know yet whether it's visible or not, depending on what kind
58     // of lockscreen flags the app might set during its first relayout.
59     private final ArrayMap<AppWindowToken, Integer> mUnknownApps = new ArrayMap<>();
60 
61     private final WindowManagerService mService;
62 
UnknownAppVisibilityController(WindowManagerService service)63     UnknownAppVisibilityController(WindowManagerService service) {
64         mService = service;
65     }
66 
allResolved()67     boolean allResolved() {
68         return mUnknownApps.isEmpty();
69     }
70 
clear()71     void clear() {
72         mUnknownApps.clear();
73     }
74 
getDebugMessage()75     String getDebugMessage() {
76         final StringBuilder builder = new StringBuilder();
77         for (int i = mUnknownApps.size() - 1; i >= 0; i--) {
78             builder.append("app=").append(mUnknownApps.keyAt(i))
79                     .append(" state=").append(mUnknownApps.valueAt(i));
80             if (i != 0) {
81                 builder.append(' ');
82             }
83         }
84         return builder.toString();
85     }
86 
appRemovedOrHidden(@onNull AppWindowToken appWindow)87     void appRemovedOrHidden(@NonNull AppWindowToken appWindow) {
88         if (DEBUG_UNKNOWN_APP_VISIBILITY) {
89             Slog.d(TAG, "App removed or hidden appWindow=" + appWindow);
90         }
91         mUnknownApps.remove(appWindow);
92     }
93 
94     /**
95      * Notifies that {@param appWindow} has been launched behind Keyguard, and we need to wait until
96      * it is resumed and relaid out to resolve the visibility.
97      */
notifyLaunched(@onNull AppWindowToken appWindow)98     void notifyLaunched(@NonNull AppWindowToken appWindow) {
99         if (DEBUG_UNKNOWN_APP_VISIBILITY) {
100             Slog.d(TAG, "App launched appWindow=" + appWindow);
101         }
102         mUnknownApps.put(appWindow, UNKNOWN_STATE_WAITING_RESUME);
103     }
104 
105     /**
106      * Notifies that {@param appWindow} has finished resuming.
107      */
notifyAppResumedFinished(@onNull AppWindowToken appWindow)108     void notifyAppResumedFinished(@NonNull AppWindowToken appWindow) {
109         if (mUnknownApps.containsKey(appWindow)
110                 && mUnknownApps.get(appWindow) == UNKNOWN_STATE_WAITING_RESUME) {
111             if (DEBUG_UNKNOWN_APP_VISIBILITY) {
112                 Slog.d(TAG, "App resume finished appWindow=" + appWindow);
113             }
114             mUnknownApps.put(appWindow, UNKNOWN_STATE_WAITING_RELAYOUT);
115         }
116     }
117 
118     /**
119      * Notifies that {@param appWindow} has relaid out.
120      */
notifyRelayouted(@onNull AppWindowToken appWindow)121     void notifyRelayouted(@NonNull AppWindowToken appWindow) {
122         if (!mUnknownApps.containsKey(appWindow)) {
123             return;
124         }
125         if (DEBUG_UNKNOWN_APP_VISIBILITY) {
126             Slog.d(TAG, "App relayouted appWindow=" + appWindow);
127         }
128         int state = mUnknownApps.get(appWindow);
129         if (state == UNKNOWN_STATE_WAITING_RELAYOUT) {
130             mUnknownApps.put(appWindow, UNKNOWN_STATE_WAITING_VISIBILITY_UPDATE);
131             mService.notifyKeyguardFlagsChanged(this::notifyVisibilitiesUpdated);
132         }
133     }
134 
notifyVisibilitiesUpdated()135     private void notifyVisibilitiesUpdated() {
136         if (DEBUG_UNKNOWN_APP_VISIBILITY) {
137             Slog.d(TAG, "Visibility updated DONE");
138         }
139         boolean changed = false;
140         for (int i = mUnknownApps.size() - 1; i >= 0; i--) {
141             if (mUnknownApps.valueAt(i) == UNKNOWN_STATE_WAITING_VISIBILITY_UPDATE) {
142                 mUnknownApps.removeAt(i);
143                 changed = true;
144             }
145         }
146         if (changed) {
147             mService.mWindowPlacerLocked.performSurfacePlacement();
148         }
149     }
150 
dump(PrintWriter pw, String prefix)151     void dump(PrintWriter pw, String prefix) {
152         if (mUnknownApps.isEmpty()) {
153             return;
154         }
155         pw.println(prefix + "Unknown visibilities:");
156         for (int i = mUnknownApps.size() - 1; i >= 0; i--) {
157             pw.println(prefix + "  app=" + mUnknownApps.keyAt(i)
158                     + " state=" + mUnknownApps.valueAt(i));
159         }
160     }
161 }
162