1 /* 2 * Copyright (C) 2019 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.systemui.statusbar; 18 19 import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD; 20 import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_TO_AOD; 21 import static com.android.systemui.keyguard.shared.model.KeyguardState.GONE; 22 import static com.android.systemui.util.kotlin.JavaAdapterKt.combineFlows; 23 24 import android.animation.Animator; 25 import android.animation.AnimatorListenerAdapter; 26 import android.animation.ObjectAnimator; 27 import android.animation.ValueAnimator; 28 import android.os.SystemProperties; 29 import android.os.Trace; 30 import android.text.format.DateFormat; 31 import android.util.FloatProperty; 32 import android.util.Log; 33 import android.view.Choreographer; 34 import android.view.View; 35 import android.view.animation.Interpolator; 36 37 import androidx.annotation.NonNull; 38 39 import com.android.app.animation.Interpolators; 40 import com.android.compose.animation.scene.SceneKey; 41 import com.android.internal.annotations.GuardedBy; 42 import com.android.internal.annotations.VisibleForTesting; 43 import com.android.internal.jank.InteractionJankMonitor; 44 import com.android.internal.jank.InteractionJankMonitor.Configuration; 45 import com.android.internal.logging.UiEventLogger; 46 import com.android.keyguard.KeyguardClockSwitch; 47 import com.android.systemui.DejankUtils; 48 import com.android.systemui.dagger.SysUISingleton; 49 import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor; 50 import com.android.systemui.deviceentry.shared.model.DeviceUnlockStatus; 51 import com.android.systemui.keyguard.MigrateClocksToBlueprint; 52 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor; 53 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; 54 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; 55 import com.android.systemui.res.R; 56 import com.android.systemui.scene.domain.interactor.SceneInteractor; 57 import com.android.systemui.scene.shared.flag.SceneContainerFlag; 58 import com.android.systemui.scene.shared.model.Scenes; 59 import com.android.systemui.shade.domain.interactor.ShadeInteractor; 60 import com.android.systemui.statusbar.notification.stack.StackStateAnimator; 61 import com.android.systemui.statusbar.policy.CallbackController; 62 import com.android.systemui.util.Compile; 63 import com.android.systemui.util.kotlin.JavaAdapter; 64 65 import com.google.common.base.Preconditions; 66 67 import dagger.Lazy; 68 69 import java.io.PrintWriter; 70 import java.util.ArrayList; 71 import java.util.Comparator; 72 import java.util.Map; 73 74 import javax.inject.Inject; 75 76 /** 77 * Tracks and reports on {@link StatusBarState}. 78 */ 79 @SysUISingleton 80 public class StatusBarStateControllerImpl implements 81 SysuiStatusBarStateController, 82 CallbackController<StateListener> { 83 private static final String TAG = "SbStateController"; 84 private static final boolean DEBUG_IMMERSIVE_APPS = 85 SystemProperties.getBoolean("persist.debug.immersive_apps", false); 86 87 // Must be a power of 2 88 private static final int HISTORY_SIZE = 32; 89 90 private static final int MAX_STATE = StatusBarState.SHADE_LOCKED; 91 private static final int MIN_STATE = StatusBarState.SHADE; 92 93 private static final Comparator<RankedListener> sComparator = 94 Comparator.comparingInt(o -> o.mRank); 95 private static final FloatProperty<StatusBarStateControllerImpl> SET_DARK_AMOUNT_PROPERTY = 96 new FloatProperty<StatusBarStateControllerImpl>("mDozeAmount") { 97 98 @Override 99 public void setValue(StatusBarStateControllerImpl object, float value) { 100 object.setDozeAmountInternal(value); 101 } 102 103 @Override 104 public Float get(StatusBarStateControllerImpl object) { 105 return object.mDozeAmount; 106 } 107 }; 108 109 private final ArrayList<RankedListener> mListeners = new ArrayList<>(); 110 private final UiEventLogger mUiEventLogger; 111 private final Lazy<InteractionJankMonitor> mInteractionJankMonitorLazy; 112 private final JavaAdapter mJavaAdapter; 113 private final Lazy<KeyguardTransitionInteractor> mKeyguardTransitionInteractorLazy; 114 private final Lazy<ShadeInteractor> mShadeInteractorLazy; 115 private final Lazy<DeviceUnlockedInteractor> mDeviceUnlockedInteractorLazy; 116 private final Lazy<SceneInteractor> mSceneInteractorLazy; 117 private final Lazy<KeyguardClockInteractor> mKeyguardClockInteractorLazy; 118 private int mState; 119 private int mLastState; 120 private int mUpcomingState; 121 private boolean mLeaveOpenOnKeyguardHide; 122 private boolean mKeyguardRequested; 123 124 // Record the HISTORY_SIZE most recent states 125 private int mHistoryIndex = 0; 126 private HistoricalState[] mHistoricalRecords = new HistoricalState[HISTORY_SIZE]; 127 // These views are used by InteractionJankMonitor to get callback from HWUI. 128 private View mView; 129 private KeyguardClockSwitch mClockSwitchView; 130 131 /** 132 * If any of the system bars is hidden. 133 */ 134 private boolean mIsFullscreen = false; 135 136 /** 137 * If the device is currently pulsing (AOD2). 138 */ 139 private boolean mPulsing; 140 141 /** 142 * If the device is currently dozing or not. 143 */ 144 private boolean mIsDozing; 145 146 /** 147 * If the device is currently dreaming or not. 148 */ 149 private boolean mIsDreaming; 150 151 /** 152 * If the status bar is currently expanded or not. 153 */ 154 private boolean mIsExpanded; 155 156 /** 157 * Current {@link #mDozeAmount} animator. 158 */ 159 private ValueAnimator mDarkAnimator; 160 161 /** 162 * Current doze amount in this frame. 163 */ 164 private float mDozeAmount; 165 166 /** 167 * Where the animator will stop. 168 */ 169 private float mDozeAmountTarget; 170 171 /** 172 * The type of interpolator that should be used to the doze animation. 173 */ 174 private Interpolator mDozeInterpolator = Interpolators.FAST_OUT_SLOW_IN; 175 176 @Inject StatusBarStateControllerImpl( UiEventLogger uiEventLogger, Lazy<InteractionJankMonitor> interactionJankMonitorLazy, JavaAdapter javaAdapter, Lazy<KeyguardTransitionInteractor> keyguardTransitionInteractor, Lazy<ShadeInteractor> shadeInteractorLazy, Lazy<DeviceUnlockedInteractor> deviceUnlockedInteractorLazy, Lazy<SceneInteractor> sceneInteractorLazy, Lazy<KeyguardClockInteractor> keyguardClockInteractorLazy)177 public StatusBarStateControllerImpl( 178 UiEventLogger uiEventLogger, 179 Lazy<InteractionJankMonitor> interactionJankMonitorLazy, 180 JavaAdapter javaAdapter, 181 Lazy<KeyguardTransitionInteractor> keyguardTransitionInteractor, 182 Lazy<ShadeInteractor> shadeInteractorLazy, 183 Lazy<DeviceUnlockedInteractor> deviceUnlockedInteractorLazy, 184 Lazy<SceneInteractor> sceneInteractorLazy, 185 Lazy<KeyguardClockInteractor> keyguardClockInteractorLazy) { 186 mUiEventLogger = uiEventLogger; 187 mInteractionJankMonitorLazy = interactionJankMonitorLazy; 188 mJavaAdapter = javaAdapter; 189 mKeyguardTransitionInteractorLazy = keyguardTransitionInteractor; 190 mShadeInteractorLazy = shadeInteractorLazy; 191 mDeviceUnlockedInteractorLazy = deviceUnlockedInteractorLazy; 192 mSceneInteractorLazy = sceneInteractorLazy; 193 mKeyguardClockInteractorLazy = keyguardClockInteractorLazy; 194 for (int i = 0; i < HISTORY_SIZE; i++) { 195 mHistoricalRecords[i] = new HistoricalState(); 196 } 197 } 198 199 @Override start()200 public void start() { 201 mJavaAdapter.alwaysCollectFlow( 202 mKeyguardTransitionInteractorLazy.get().isFinishedInState(GONE), 203 (Boolean isFinishedInState) -> { 204 if (isFinishedInState) { 205 setLeaveOpenOnKeyguardHide(false); 206 } 207 }); 208 209 mJavaAdapter.alwaysCollectFlow(mShadeInteractorLazy.get().isAnyExpanded(), 210 this::onShadeOrQsExpanded); 211 212 if (SceneContainerFlag.isEnabled()) { 213 mJavaAdapter.alwaysCollectFlow( 214 combineFlows( 215 mDeviceUnlockedInteractorLazy.get().getDeviceUnlockStatus(), 216 mSceneInteractorLazy.get().getCurrentScene(), 217 this::calculateStateFromSceneFramework), 218 this::onStatusBarStateChanged); 219 } 220 } 221 222 @Override getState()223 public int getState() { 224 return mState; 225 } 226 227 @Override setState(int state, boolean force)228 public boolean setState(int state, boolean force) { 229 if (SceneContainerFlag.isEnabled()) { 230 return false; 231 } 232 233 if (state > MAX_STATE || state < MIN_STATE) { 234 throw new IllegalArgumentException("Invalid state " + state); 235 } 236 237 // Unless we're explicitly asked to force the state change, don't apply the new state if 238 // it's identical to both the current and upcoming states, since that should not be 239 // necessary. 240 if (!force && state == mState && state == mUpcomingState) { 241 return false; 242 } 243 244 updateStateAndNotifyListeners(state); 245 return true; 246 } 247 248 /** 249 * Updates the {@link StatusBarState} and notifies registered listeners, if needed. 250 */ updateStateAndNotifyListeners(int state)251 private void updateStateAndNotifyListeners(int state) { 252 if (state != mUpcomingState) { 253 Log.d(TAG, "setState: requested state " + StatusBarState.toString(state) 254 + "!= upcomingState: " + StatusBarState.toString(mUpcomingState) + ". " 255 + "This usually means the status bar state transition was interrupted before " 256 + "the upcoming state could be applied."); 257 } 258 259 // Record the to-be mState and mLastState 260 recordHistoricalState(state /* newState */, mState /* lastState */, false); 261 262 // b/139259891 263 if (mState == StatusBarState.SHADE && state == StatusBarState.SHADE_LOCKED) { 264 Log.e(TAG, "Invalid state transition: SHADE -> SHADE_LOCKED", new Throwable()); 265 } 266 267 synchronized (mListeners) { 268 String tag = getClass().getSimpleName() + "#setState(" + state + ")"; 269 DejankUtils.startDetectingBlockingIpcs(tag); 270 for (RankedListener rl : new ArrayList<>(mListeners)) { 271 rl.mListener.onStatePreChange(mState, state); 272 } 273 mLastState = mState; 274 mState = state; 275 updateUpcomingState(mState); 276 mUiEventLogger.log(StatusBarStateEvent.fromState(mState)); 277 Trace.instantForTrack(Trace.TRACE_TAG_APP, "UI Events", "StatusBarState " + tag); 278 for (RankedListener rl : new ArrayList<>(mListeners)) { 279 rl.mListener.onStateChanged(mState); 280 } 281 282 for (RankedListener rl : new ArrayList<>(mListeners)) { 283 rl.mListener.onStatePostChange(); 284 } 285 DejankUtils.stopDetectingBlockingIpcs(tag); 286 } 287 } 288 289 @Override setUpcomingState(int nextState)290 public void setUpcomingState(int nextState) { 291 if (SceneContainerFlag.isEnabled()) { 292 return; 293 } 294 295 recordHistoricalState(nextState /* newState */, mState /* lastState */, true); 296 updateUpcomingState(nextState); 297 } 298 updateUpcomingState(int upcomingState)299 private void updateUpcomingState(int upcomingState) { 300 if (mUpcomingState != upcomingState) { 301 mUpcomingState = upcomingState; 302 for (RankedListener rl : new ArrayList<>(mListeners)) { 303 rl.mListener.onUpcomingStateChanged(mUpcomingState); 304 } 305 } 306 } 307 308 @Override getCurrentOrUpcomingState()309 public int getCurrentOrUpcomingState() { 310 return mUpcomingState; 311 } 312 313 @Override isDozing()314 public boolean isDozing() { 315 return mIsDozing; 316 } 317 318 @Override isPulsing()319 public boolean isPulsing() { 320 return mPulsing; 321 } 322 323 @Override getDozeAmount()324 public float getDozeAmount() { 325 return mDozeAmount; 326 } 327 328 @Override isExpanded()329 public boolean isExpanded() { 330 return mIsExpanded; 331 } 332 333 @Override getInterpolatedDozeAmount()334 public float getInterpolatedDozeAmount() { 335 return mDozeInterpolator.getInterpolation(mDozeAmount); 336 } 337 338 @Override setIsDozing(boolean isDozing)339 public boolean setIsDozing(boolean isDozing) { 340 if (mIsDozing == isDozing) { 341 return false; 342 } 343 344 mIsDozing = isDozing; 345 346 synchronized (mListeners) { 347 String tag = getClass().getSimpleName() + "#setIsDozing"; 348 DejankUtils.startDetectingBlockingIpcs(tag); 349 for (RankedListener rl : new ArrayList<>(mListeners)) { 350 rl.mListener.onDozingChanged(isDozing); 351 } 352 DejankUtils.stopDetectingBlockingIpcs(tag); 353 } 354 355 return true; 356 } 357 358 @Override setIsDreaming(boolean isDreaming)359 public boolean setIsDreaming(boolean isDreaming) { 360 if (Log.isLoggable(TAG, Log.DEBUG) || Compile.IS_DEBUG) { 361 Log.d(TAG, "setIsDreaming:" + isDreaming); 362 } 363 if (mIsDreaming == isDreaming) { 364 return false; 365 } 366 367 mIsDreaming = isDreaming; 368 369 synchronized (mListeners) { 370 String tag = getClass().getSimpleName() + "#setIsDreaming"; 371 DejankUtils.startDetectingBlockingIpcs(tag); 372 for (RankedListener rl : new ArrayList<>(mListeners)) { 373 rl.mListener.onDreamingChanged(isDreaming); 374 } 375 DejankUtils.stopDetectingBlockingIpcs(tag); 376 } 377 378 return true; 379 } 380 381 @Override isDreaming()382 public boolean isDreaming() { 383 return mIsDreaming; 384 } 385 386 @Override setAndInstrumentDozeAmount(View view, float dozeAmount, boolean animated)387 public void setAndInstrumentDozeAmount(View view, float dozeAmount, boolean animated) { 388 if (mDarkAnimator != null && mDarkAnimator.isRunning()) { 389 if (animated && mDozeAmountTarget == dozeAmount) { 390 return; 391 } else { 392 mDarkAnimator.cancel(); 393 } 394 } 395 396 // We don't need a new attached view if we already have one. 397 if ((mView == null || !mView.isAttachedToWindow()) 398 && (view != null && view.isAttachedToWindow())) { 399 mView = view; 400 mClockSwitchView = view.findViewById(R.id.keyguard_clock_container); 401 } 402 mDozeAmountTarget = dozeAmount; 403 if (animated) { 404 startDozeAnimation(); 405 } else { 406 setDozeAmountInternal(dozeAmount); 407 } 408 } 409 onShadeOrQsExpanded(Boolean isExpanded)410 private void onShadeOrQsExpanded(Boolean isExpanded) { 411 if (mIsExpanded != isExpanded) { 412 mIsExpanded = isExpanded; 413 String tag = getClass().getSimpleName() + "#setIsExpanded"; 414 DejankUtils.startDetectingBlockingIpcs(tag); 415 for (RankedListener rl : new ArrayList<>(mListeners)) { 416 rl.mListener.onExpandedChanged(mIsExpanded); 417 } 418 DejankUtils.stopDetectingBlockingIpcs(tag); 419 } 420 } 421 startDozeAnimation()422 private void startDozeAnimation() { 423 if (mDozeAmount == 0f || mDozeAmount == 1f) { 424 mDozeInterpolator = mIsDozing 425 ? Interpolators.FAST_OUT_SLOW_IN 426 : Interpolators.TOUCH_RESPONSE_REVERSE; 427 } 428 if (mDozeAmount == 1f && !mIsDozing) { 429 // Workaround to force relayoutWindow to be called a frame earlier. Otherwise, if 430 // mDozeAmount = 1f, then neither start() nor the first frame of the animation will 431 // cause the scrim opacity to change, which ultimately results in an extra relayout and 432 // causes us to miss a frame. By settings the doze amount to be <1f a frame earlier, 433 // we can batch the relayout with the one in NotificationShadeWindowControllerImpl. 434 setDozeAmountInternal(0.99f); 435 } 436 mDarkAnimator = createDarkAnimator(); 437 } 438 439 @VisibleForTesting createDarkAnimator()440 protected ObjectAnimator createDarkAnimator() { 441 ObjectAnimator darkAnimator = ObjectAnimator.ofFloat( 442 this, SET_DARK_AMOUNT_PROPERTY, mDozeAmountTarget); 443 darkAnimator.setInterpolator(Interpolators.LINEAR); 444 darkAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP); 445 darkAnimator.addListener(new AnimatorListenerAdapter() { 446 @Override 447 public void onAnimationCancel(Animator animation) { 448 cancelInteractionJankMonitor(); 449 } 450 451 @Override 452 public void onAnimationEnd(Animator animation) { 453 endInteractionJankMonitor(); 454 } 455 456 @Override 457 public void onAnimationStart(Animator animation) { 458 beginInteractionJankMonitor(); 459 } 460 }); 461 darkAnimator.start(); 462 return darkAnimator; 463 } 464 setDozeAmountInternal(float dozeAmount)465 private void setDozeAmountInternal(float dozeAmount) { 466 if (Float.compare(dozeAmount, mDozeAmount) == 0) { 467 return; 468 } 469 mDozeAmount = dozeAmount; 470 float interpolatedAmount = mDozeInterpolator.getInterpolation(dozeAmount); 471 synchronized (mListeners) { 472 String tag = getClass().getSimpleName() + "#setDozeAmount"; 473 DejankUtils.startDetectingBlockingIpcs(tag); 474 for (RankedListener rl : new ArrayList<>(mListeners)) { 475 rl.mListener.onDozeAmountChanged(mDozeAmount, interpolatedAmount); 476 } 477 DejankUtils.stopDetectingBlockingIpcs(tag); 478 } 479 } 480 481 /** Returns the id of the currently rendering clock */ getClockId()482 public String getClockId() { 483 if (MigrateClocksToBlueprint.isEnabled()) { 484 return mKeyguardClockInteractorLazy.get().getRenderedClockId(); 485 } 486 487 if (mClockSwitchView == null) { 488 Log.e(TAG, "Clock container was missing"); 489 return KeyguardClockSwitch.MISSING_CLOCK_ID; 490 } 491 492 return mClockSwitchView.getClockId(); 493 } 494 beginInteractionJankMonitor()495 private void beginInteractionJankMonitor() { 496 final boolean shouldPost = 497 (mIsDozing && mDozeAmount == 0) || (!mIsDozing && mDozeAmount == 1); 498 InteractionJankMonitor monitor = mInteractionJankMonitorLazy.get(); 499 if (monitor != null && mView != null && mView.isAttachedToWindow()) { 500 if (shouldPost) { 501 Choreographer.getInstance().postCallback( 502 Choreographer.CALLBACK_ANIMATION, this::beginInteractionJankMonitor, null); 503 } else { 504 Configuration.Builder builder = Configuration.Builder.withView(getCujType(), mView) 505 .setTag(getClockId()) 506 .setDeferMonitorForAnimationStart(false); 507 monitor.begin(builder); 508 } 509 } 510 } 511 endInteractionJankMonitor()512 private void endInteractionJankMonitor() { 513 InteractionJankMonitor monitor = mInteractionJankMonitorLazy.get(); 514 if (monitor == null) { 515 return; 516 } 517 monitor.end(getCujType()); 518 } 519 cancelInteractionJankMonitor()520 private void cancelInteractionJankMonitor() { 521 InteractionJankMonitor monitor = mInteractionJankMonitorLazy.get(); 522 if (monitor == null) { 523 return; 524 } 525 monitor.cancel(getCujType()); 526 } 527 getCujType()528 private int getCujType() { 529 return mIsDozing ? CUJ_LOCKSCREEN_TRANSITION_TO_AOD : CUJ_LOCKSCREEN_TRANSITION_FROM_AOD; 530 } 531 532 @Override goingToFullShade()533 public boolean goingToFullShade() { 534 return getState() == StatusBarState.SHADE && mLeaveOpenOnKeyguardHide; 535 } 536 537 @Override setLeaveOpenOnKeyguardHide(boolean leaveOpen)538 public void setLeaveOpenOnKeyguardHide(boolean leaveOpen) { 539 mLeaveOpenOnKeyguardHide = leaveOpen; 540 } 541 542 @Override leaveOpenOnKeyguardHide()543 public boolean leaveOpenOnKeyguardHide() { 544 return mLeaveOpenOnKeyguardHide; 545 } 546 547 @Override fromShadeLocked()548 public boolean fromShadeLocked() { 549 return mLastState == StatusBarState.SHADE_LOCKED; 550 } 551 552 @Override addCallback(@onNull StateListener listener)553 public void addCallback(@NonNull StateListener listener) { 554 synchronized (mListeners) { 555 addListenerInternalLocked(listener, Integer.MAX_VALUE); 556 } 557 } 558 559 /** 560 * Add a listener and a rank based on the priority of this message 561 * @param listener the listener 562 * @param rank the order in which you'd like to be called. Ranked listeners will be 563 * notified before unranked, and we will sort ranked listeners from low to high 564 * 565 * @deprecated This method exists only to solve latent inter-dependencies from refactoring 566 * StatusBarState out of CentralSurfaces.java. Any new listeners should be built not to need 567 * ranking (i.e., they are non-dependent on the order of operations of StatusBarState 568 * listeners). 569 */ 570 @Deprecated 571 @Override addCallback(StateListener listener, @SbStateListenerRank int rank)572 public void addCallback(StateListener listener, @SbStateListenerRank int rank) { 573 synchronized (mListeners) { 574 addListenerInternalLocked(listener, rank); 575 } 576 } 577 578 @GuardedBy("mListeners") addListenerInternalLocked(StateListener listener, int rank)579 private void addListenerInternalLocked(StateListener listener, int rank) { 580 // Protect against double-subscribe 581 for (RankedListener rl : mListeners) { 582 if (rl.mListener.equals(listener)) { 583 return; 584 } 585 } 586 587 RankedListener rl = new SysuiStatusBarStateController.RankedListener(listener, rank); 588 mListeners.add(rl); 589 mListeners.sort(sComparator); 590 } 591 592 593 @Override removeCallback(@onNull StateListener listener)594 public void removeCallback(@NonNull StateListener listener) { 595 synchronized (mListeners) { 596 mListeners.removeIf((it) -> it.mListener.equals(listener)); 597 } 598 } 599 600 @Override setKeyguardRequested(boolean keyguardRequested)601 public void setKeyguardRequested(boolean keyguardRequested) { 602 mKeyguardRequested = keyguardRequested; 603 } 604 605 @Override isKeyguardRequested()606 public boolean isKeyguardRequested() { 607 return mKeyguardRequested; 608 } 609 610 @Override setPulsing(boolean pulsing)611 public void setPulsing(boolean pulsing) { 612 if (mPulsing != pulsing) { 613 mPulsing = pulsing; 614 synchronized (mListeners) { 615 for (RankedListener rl : new ArrayList<>(mListeners)) { 616 rl.mListener.onPulsingChanged(pulsing); 617 } 618 } 619 } 620 } 621 622 /** 623 * Returns String readable state of status bar from {@link StatusBarState} 624 */ describe(int state)625 public static String describe(int state) { 626 return StatusBarState.toString(state); 627 } 628 629 @Override dump(PrintWriter pw, String[] args)630 public void dump(PrintWriter pw, String[] args) { 631 pw.println("StatusBarStateController: "); 632 pw.println(" mState=" + mState + " (" + describe(mState) + ")"); 633 pw.println(" mLastState=" + mLastState + " (" + describe(mLastState) + ")"); 634 pw.println(" mLeaveOpenOnKeyguardHide=" + mLeaveOpenOnKeyguardHide); 635 pw.println(" mKeyguardRequested=" + mKeyguardRequested); 636 pw.println(" mIsDozing=" + mIsDozing); 637 pw.println(" mIsDreaming=" + mIsDreaming); 638 pw.println(" mListeners{" + mListeners.size() + "}="); 639 for (RankedListener rl : mListeners) { 640 pw.println(" " + rl.mListener); 641 } 642 pw.println(" Historical states:"); 643 // Ignore records without a timestamp 644 int size = 0; 645 for (int i = 0; i < HISTORY_SIZE; i++) { 646 if (mHistoricalRecords[i].mTimestamp != 0) size++; 647 } 648 for (int i = mHistoryIndex + HISTORY_SIZE; 649 i >= mHistoryIndex + HISTORY_SIZE - size + 1; i--) { 650 pw.println(" (" + (mHistoryIndex + HISTORY_SIZE - i + 1) + ")" 651 + mHistoricalRecords[i & (HISTORY_SIZE - 1)]); 652 } 653 } 654 recordHistoricalState(int newState, int lastState, boolean upcoming)655 private void recordHistoricalState(int newState, int lastState, boolean upcoming) { 656 Trace.traceCounter(Trace.TRACE_TAG_APP, "statusBarState", newState); 657 mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE; 658 HistoricalState state = mHistoricalRecords[mHistoryIndex]; 659 state.mNewState = newState; 660 state.mLastState = lastState; 661 state.mTimestamp = System.currentTimeMillis(); 662 state.mUpcoming = upcoming; 663 } 664 calculateStateFromSceneFramework( DeviceUnlockStatus deviceUnlockStatus, SceneKey currentScene)665 private int calculateStateFromSceneFramework( 666 DeviceUnlockStatus deviceUnlockStatus, 667 SceneKey currentScene) { 668 SceneContainerFlag.isUnexpectedlyInLegacyMode(); 669 670 if (deviceUnlockStatus.isUnlocked()) { 671 return StatusBarState.SHADE; 672 } else { 673 return Preconditions.checkNotNull(sStatusBarStateByLockedSceneKey.get(currentScene)); 674 } 675 } 676 677 /** Notifies that the {@link StatusBarState} has changed to the given new state. */ onStatusBarStateChanged(int newState)678 private void onStatusBarStateChanged(int newState) { 679 SceneContainerFlag.isUnexpectedlyInLegacyMode(); 680 681 if (newState == mState) { 682 return; 683 } 684 685 updateStateAndNotifyListeners(newState); 686 } 687 688 private static final Map<SceneKey, Integer> sStatusBarStateByLockedSceneKey = Map.of( 689 Scenes.Lockscreen, StatusBarState.KEYGUARD, 690 Scenes.Bouncer, StatusBarState.KEYGUARD, 691 Scenes.Communal, StatusBarState.KEYGUARD, 692 Scenes.Shade, StatusBarState.SHADE_LOCKED, 693 Scenes.NotificationsShade, StatusBarState.SHADE_LOCKED, 694 Scenes.QuickSettings, StatusBarState.SHADE_LOCKED, 695 Scenes.QuickSettingsShade, StatusBarState.SHADE_LOCKED, 696 Scenes.Gone, StatusBarState.SHADE 697 ); 698 699 /** 700 * For keeping track of our previous state to help with debugging 701 */ 702 private static class HistoricalState { 703 int mNewState; 704 int mLastState; 705 long mTimestamp; 706 boolean mUpcoming; 707 708 @Override toString()709 public String toString() { 710 if (mTimestamp != 0) { 711 StringBuilder sb = new StringBuilder(); 712 if (mUpcoming) { 713 sb.append("upcoming-"); 714 } 715 sb.append("newState=").append(mNewState) 716 .append("(").append(describe(mNewState)).append(")"); 717 sb.append(" lastState=").append(mLastState).append("(").append(describe(mLastState)) 718 .append(")"); 719 sb.append(" timestamp=") 720 .append(DateFormat.format("MM-dd HH:mm:ss", mTimestamp)); 721 722 return sb.toString(); 723 } 724 return "Empty " + getClass().getSimpleName(); 725 } 726 } 727 } 728