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