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
17package com.android.server.wm;
18
19import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_UNKNOWN_APP_VISIBILITY;
20import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
21import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
22
23import android.annotation.NonNull;
24import android.util.ArrayMap;
25import android.util.Slog;
26
27import com.android.server.wm.WindowManagerService.H;
28
29import 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 */
37class 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
63    UnknownAppVisibilityController(WindowManagerService service) {
64        mService = service;
65    }
66
67    boolean allResolved() {
68        return mUnknownApps.isEmpty();
69    }
70
71    void clear() {
72        mUnknownApps.clear();
73    }
74
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
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     */
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     */
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     */
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
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
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