Optimize latency when unlocking phone
Latency when unlocking the phone regressed a bit for two reasons:
- For lockscreen -> app we now have to create a full starting
window containing the snapshot, while previously this was just
showing a surface.
- For lockscreen -> home, we can't use the saved surface anymore
because currently we don't support snapshotting translucent
activities. However, in the long term, we want home screen to be
more involved into transitions anyways, so we'll have to wait for
the first frame draw anyways.
However, crystal ball trainee developer Jorim added some
artificial latency in this transition 3 years ago, because he knew
that it is going to be an issue at some point so we have some
headroom to improve! Genius! On a more serious note, it was because
he didn't understand how to read systraces with binders involved (to
be fair, there was also no binder tracing).
Now, we can completely fix the introduces latencies above by
removing this latency of 100ms, and we are 30-70ms better than
before! However, this requires a lot of discipline in SystemUI.
Currently, the callback to dismiss Keyguard takes around 30ms. By
moving all non-essential binder calls of the main thread or to the
next frame, we bring this down to 5ms, such that window animation
and Keyguard animation starts about at the same time.
Test: Take systrace, unlock phone...profit!
Change-Id: I3ea672bc2eca47221bc6c9f3d7c56b6899df207d
Fixes: 38294347
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index de2bdde..2094362d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -280,7 +280,7 @@
}
KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
- int userId = ActivityManager.getCurrentUser();
+ int userId = KeyguardUpdateMonitor.getCurrentUser();
String trustIndication = getTrustIndication();
if (!mUserManager.isUserUnlocked(userId)) {
mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked);
@@ -379,6 +379,9 @@
};
public void setDozing(boolean dozing) {
+ if (mDozing == dozing) {
+ return;
+ }
mDozing = dozing;
updateIndication();
updateDisclosure();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 2124011..95f32bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -18,6 +18,7 @@
import android.app.ActivityManager;
import android.content.Context;
+import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
@@ -55,6 +56,7 @@
protected final ViewGroup mContainer;
private final FalsingManager mFalsingManager;
private final DismissCallbackRegistry mDismissCallbackRegistry;
+ private final Handler mHandler;
protected KeyguardHostView mKeyguardView;
protected ViewGroup mRoot;
private boolean mShowingSoon;
@@ -66,6 +68,7 @@
mBouncerPromptReason = mCallback.getBouncerPromptReason();
}
};
+ private final Runnable mRemoveViewRunnable = this::removeView;
public KeyguardBouncer(Context context, ViewMediatorCallback callback,
LockPatternUtils lockPatternUtils, ViewGroup container,
@@ -77,6 +80,7 @@
KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback);
mFalsingManager = FalsingManager.getInstance(mContext);
mDismissCallbackRegistry = dismissCallbackRegistry;
+ mHandler = new Handler();
}
public void show(boolean resetSecuritySelection) {
@@ -179,10 +183,14 @@
mKeyguardView.cancelDismissAction();
mKeyguardView.cleanUp();
}
- if (destroyView) {
- removeView();
- } else if (mRoot != null) {
+ if (mRoot != null) {
mRoot.setVisibility(View.INVISIBLE);
+ if (destroyView) {
+
+ // We have a ViewFlipper that unregisters a broadcast when being detached, which may
+ // be slow because of AM lock contention during unlocking. We can delay it a bit.
+ mHandler.postDelayed(mRemoveViewRunnable, 50);
+ }
}
}
@@ -226,6 +234,7 @@
}
protected void ensureView() {
+ mHandler.removeCallbacks(mRemoveViewRunnable);
if (mRoot == null) {
inflateView();
}
@@ -233,6 +242,7 @@
protected void inflateView() {
removeView();
+ mHandler.removeCallbacks(mRemoveViewRunnable);
mRoot = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.keyguard_bouncer, null);
mKeyguardView = (KeyguardHostView) mRoot.findViewById(R.id.keyguard_host_view);
mKeyguardView.setLockPatternUtils(mLockPatternUtils);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 4581204..52838b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -128,8 +128,7 @@
protected boolean shouldShowNotificationIcon(NotificationData.Entry entry,
NotificationData notificationData, boolean showAmbient) {
- if (notificationData.isAmbient(entry.key) && !showAmbient
- && !NotificationData.showNotificationEvenIfUnprovisioned(entry.notification)) {
+ if (notificationData.isAmbient(entry.key) && !showAmbient) {
return false;
}
if (!StatusBar.isTopLevelChild(entry)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 41fb5f7..f3ba5aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -58,6 +58,7 @@
import com.android.systemui.DockedStackExistsListener;
import com.android.systemui.R;
import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.UiOffloadThread;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -128,6 +129,7 @@
private final KeyguardMonitor mKeyguardMonitor;
private final LocationController mLocationController;
private final ArraySet<Pair<String, Integer>> mCurrentNotifs = new ArraySet<>();
+ private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
// Assume it's all good unless we hear otherwise. We don't always seem
// to get broadcasts that it *is* there.
@@ -472,30 +474,38 @@
}
private void updateManagedProfile() {
- try {
- final boolean showIcon;
- final int userId = ActivityManager.getService().getLastResumedActivityUserId();
- if (mUserManager.isManagedProfile(userId) && !mKeyguardMonitor.isShowing()) {
- showIcon = true;
- mIconController.setIcon(mSlotManagedProfile,
- R.drawable.stat_sys_managed_profile_status,
- mContext.getString(R.string.accessibility_managed_profile));
- } else if (mManagedProfileInQuietMode) {
- showIcon = true;
- mIconController.setIcon(mSlotManagedProfile,
- R.drawable.stat_sys_managed_profile_status_off,
- mContext.getString(R.string.accessibility_managed_profile));
- } else {
- showIcon = false;
+ // getLastResumedActivityUserId needds to acquire the AM lock, which may be contended in
+ // some cases. Since it doesn't really matter here whether it's updated in this frame
+ // or in the next one, we call this method from our UI offload thread.
+ mUiOffloadThread.submit(() -> {
+ final int userId;
+ try {
+ userId = ActivityManager.getService().getLastResumedActivityUserId();
+ boolean isManagedProfile = mUserManager.isManagedProfile(userId);
+ mHandler.post(() -> {
+ final boolean showIcon;
+ if (isManagedProfile && !mKeyguardMonitor.isShowing()) {
+ showIcon = true;
+ mIconController.setIcon(mSlotManagedProfile,
+ R.drawable.stat_sys_managed_profile_status,
+ mContext.getString(R.string.accessibility_managed_profile));
+ } else if (mManagedProfileInQuietMode) {
+ showIcon = true;
+ mIconController.setIcon(mSlotManagedProfile,
+ R.drawable.stat_sys_managed_profile_status_off,
+ mContext.getString(R.string.accessibility_managed_profile));
+ } else {
+ showIcon = false;
+ }
+ if (mManagedProfileIconVisible != showIcon) {
+ mIconController.setIconVisibility(mSlotManagedProfile, showIcon);
+ mManagedProfileIconVisible = showIcon;
+ }
+ });
+ } catch (RemoteException e) {
+ Log.w(TAG, "updateManagedProfile: ", e);
}
- if (mManagedProfileIconVisible != showIcon) {
- mIconController.setIconVisibility(mSlotManagedProfile, showIcon);
- mManagedProfileIconVisible = showIcon;
- }
- } catch (RemoteException ex) {
- Log.w(TAG, "updateManagedProfile: ", ex);
- // ignore
- }
+ });
}
private void updateForegroundInstantApps() {
@@ -503,26 +513,22 @@
ArraySet<Pair<String, Integer>> notifs = new ArraySet<>(mCurrentNotifs);
IPackageManager pm = AppGlobals.getPackageManager();
mCurrentNotifs.clear();
- try {
- ArraySet<Integer> stacksToCheck = new ArraySet<>();
- int[] STACKS_TO_CHECK = new int[]{
- StackId.FULLSCREEN_WORKSPACE_STACK_ID,
- StackId.DOCKED_STACK_ID,
- };
- int focusedId = ActivityManager.getService().getFocusedStackId();
- if (focusedId == StackId.FULLSCREEN_WORKSPACE_STACK_ID
- || focusedId == StackId.FULLSCREEN_WORKSPACE_STACK_ID) {
- checkStack(StackId.FULLSCREEN_WORKSPACE_STACK_ID, notifs, noMan, pm);
+ mUiOffloadThread.submit(() -> {
+ try {
+ int focusedId = ActivityManager.getService().getFocusedStackId();
+ if (focusedId == StackId.FULLSCREEN_WORKSPACE_STACK_ID) {
+ checkStack(StackId.FULLSCREEN_WORKSPACE_STACK_ID, notifs, noMan, pm);
+ }
+ if (mDockedStackExists) {
+ checkStack(StackId.DOCKED_STACK_ID, notifs, noMan, pm);
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
}
- if (mDockedStackExists) {
- checkStack(StackId.DOCKED_STACK_ID, notifs, noMan, pm);
- }
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- // Cancel all the leftover notifications that don't have a foreground process anymore.
- notifs.forEach(v -> noMan.cancelAsUser(v.first, SystemMessage.NOTE_INSTANT_APPS,
- new UserHandle(v.second)));
+ // Cancel all the leftover notifications that don't have a foreground process anymore.
+ notifs.forEach(v -> noMan.cancelAsUser(v.first, SystemMessage.NOTE_INSTANT_APPS,
+ new UserHandle(v.second)));
+ });
}
private void checkStack(int stackId, ArraySet<Pair<String, Integer>> notifs,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index d49efdc..01bab3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -95,6 +95,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.SystemService;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
@@ -164,6 +165,7 @@
import com.android.systemui.SwipeHelper;
import com.android.systemui.SystemUI;
import com.android.systemui.SystemUIFactory;
+import com.android.systemui.UiOffloadThread;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.classifier.FalsingLog;
import com.android.systemui.classifier.FalsingManager;
@@ -546,6 +548,7 @@
private ViewMediatorCallback mKeyguardViewMediatorCallback;
protected ScrimController mScrimController;
protected DozeScrimController mDozeScrimController;
+ private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
private final Runnable mAutohide = new Runnable() {
@Override
@@ -4394,7 +4397,7 @@
checkBarModes();
updateMediaMetaData(false, mState != StatusBarState.KEYGUARD);
mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
- mStatusBarKeyguardViewManager.isSecure(),
+ mUnlockMethodCache.isMethodSecure(),
mStatusBarKeyguardViewManager.isOccluded());
Trace.endSection();
}
@@ -6973,11 +6976,13 @@
@Override
public void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
- try {
- mBarService.onNotificationExpansionChanged(key, userAction, expanded);
- } catch (RemoteException e) {
- // Ignore.
- }
+ mUiOffloadThread.submit(() -> {
+ try {
+ mBarService.onNotificationExpansionChanged(key, userAction, expanded);
+ } catch (RemoteException e) {
+ // Ignore.
+ }
+ });
}
public boolean isKeyguardSecure() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
index d6c080a..7bce33a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
@@ -124,13 +124,11 @@
mKeyguardFadingAway = true;
mKeyguardFadingAwayDelay = delay;
mKeyguardFadingAwayDuration = fadeoutDuration;
- notifyKeyguardChanged();
}
public void notifyKeyguardDoneFading() {
mKeyguardFadingAway = false;
mKeyguardGoingAway = false;
- notifyKeyguardChanged();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 53671a1..e1acc9b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -872,7 +872,14 @@
private final KeyguardMonitor.Callback mCallback = new KeyguardMonitor.Callback() {
@Override
public void onKeyguardShowingChanged() {
- notifyAdapters();
+
+ // When Keyguard is going away, we don't need to update our items immediately which
+ // helps making the transition faster.
+ if (!mKeyguardMonitor.isShowing()) {
+ mHandler.post(UserSwitcherController.this::notifyAdapters);
+ } else {
+ notifyAdapters();
+ }
}
};