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 63 private final DisplayContent mDisplayContent; 64 UnknownAppVisibilityController(WindowManagerService service, DisplayContent displayContent)65 UnknownAppVisibilityController(WindowManagerService service, DisplayContent displayContent) { 66 mService = service; 67 mDisplayContent = displayContent; 68 } 69 allResolved()70 boolean allResolved() { 71 return mUnknownApps.isEmpty(); 72 } 73 clear()74 void clear() { 75 mUnknownApps.clear(); 76 } 77 getDebugMessage()78 String getDebugMessage() { 79 final StringBuilder builder = new StringBuilder(); 80 for (int i = mUnknownApps.size() - 1; i >= 0; i--) { 81 builder.append("app=").append(mUnknownApps.keyAt(i)) 82 .append(" state=").append(mUnknownApps.valueAt(i)); 83 if (i != 0) { 84 builder.append(' '); 85 } 86 } 87 return builder.toString(); 88 } 89 appRemovedOrHidden(@onNull AppWindowToken appWindow)90 void appRemovedOrHidden(@NonNull AppWindowToken appWindow) { 91 if (DEBUG_UNKNOWN_APP_VISIBILITY) { 92 Slog.d(TAG, "App removed or hidden appWindow=" + appWindow); 93 } 94 mUnknownApps.remove(appWindow); 95 } 96 97 /** 98 * Notifies that {@param appWindow} has been launched behind Keyguard, and we need to wait until 99 * it is resumed and relaid out to resolve the visibility. 100 */ notifyLaunched(@onNull AppWindowToken appWindow)101 void notifyLaunched(@NonNull AppWindowToken appWindow) { 102 if (DEBUG_UNKNOWN_APP_VISIBILITY) { 103 Slog.d(TAG, "App launched appWindow=" + appWindow); 104 } 105 mUnknownApps.put(appWindow, UNKNOWN_STATE_WAITING_RESUME); 106 } 107 108 /** 109 * Notifies that {@param appWindow} has finished resuming. 110 */ notifyAppResumedFinished(@onNull AppWindowToken appWindow)111 void notifyAppResumedFinished(@NonNull AppWindowToken appWindow) { 112 if (mUnknownApps.containsKey(appWindow) 113 && mUnknownApps.get(appWindow) == UNKNOWN_STATE_WAITING_RESUME) { 114 if (DEBUG_UNKNOWN_APP_VISIBILITY) { 115 Slog.d(TAG, "App resume finished appWindow=" + appWindow); 116 } 117 mUnknownApps.put(appWindow, UNKNOWN_STATE_WAITING_RELAYOUT); 118 } 119 } 120 121 /** 122 * Notifies that {@param appWindow} has relaid out. 123 */ notifyRelayouted(@onNull AppWindowToken appWindow)124 void notifyRelayouted(@NonNull AppWindowToken appWindow) { 125 if (!mUnknownApps.containsKey(appWindow)) { 126 return; 127 } 128 if (DEBUG_UNKNOWN_APP_VISIBILITY) { 129 Slog.d(TAG, "App relayouted appWindow=" + appWindow); 130 } 131 int state = mUnknownApps.get(appWindow); 132 if (state == UNKNOWN_STATE_WAITING_RELAYOUT) { 133 mUnknownApps.put(appWindow, UNKNOWN_STATE_WAITING_VISIBILITY_UPDATE); 134 mService.notifyKeyguardFlagsChanged(this::notifyVisibilitiesUpdated, 135 appWindow.getDisplayContent().getDisplayId()); 136 } 137 } 138 notifyVisibilitiesUpdated()139 private void notifyVisibilitiesUpdated() { 140 if (DEBUG_UNKNOWN_APP_VISIBILITY) { 141 Slog.d(TAG, "Visibility updated DONE"); 142 } 143 boolean changed = false; 144 for (int i = mUnknownApps.size() - 1; i >= 0; i--) { 145 if (mUnknownApps.valueAt(i) == UNKNOWN_STATE_WAITING_VISIBILITY_UPDATE) { 146 mUnknownApps.removeAt(i); 147 changed = true; 148 } 149 } 150 if (changed) { 151 mService.mWindowPlacerLocked.performSurfacePlacement(); 152 } 153 } 154 dump(PrintWriter pw, String prefix)155 void dump(PrintWriter pw, String prefix) { 156 if (mUnknownApps.isEmpty()) { 157 return; 158 } 159 pw.println(prefix + "Unknown visibilities:"); 160 for (int i = mUnknownApps.size() - 1; i >= 0; i--) { 161 pw.println(prefix + " app=" + mUnknownApps.keyAt(i) 162 + " state=" + mUnknownApps.valueAt(i)); 163 } 164 } 165 } 166