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