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