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