Skip to content
This repository has been archived by the owner on Nov 8, 2023. It is now read-only.

Commit

Permalink
Optimize latency when unlocking phone
Browse files Browse the repository at this point in the history
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
  • Loading branch information
XSJoJo committed May 17, 2017
1 parent 64766b8 commit fabc743
Show file tree
Hide file tree
Showing 19 changed files with 233 additions and 101 deletions.
3 changes: 1 addition & 2 deletions core/res/res/anim/lock_screen_behind_enter.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@

<set xmlns:android="http://schemas.android.com/apk/res/android"
android:detachWallpaper="true"
android:shareInterpolator="false"
android:startOffset="100">
android:shareInterpolator="false">

<translate android:fromYDelta="110%p" android:toYDelta="0"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
Expand Down
3 changes: 1 addition & 2 deletions core/res/res/anim/lock_screen_behind_enter_fade_in.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,5 @@
android:interpolator="@interpolator/linear"
android:fromAlpha="0" android:toAlpha="1"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
android:duration="250"
android:startOffset="100">
android:duration="250">
</alpha>
4 changes: 2 additions & 2 deletions core/res/res/anim/lock_screen_behind_enter_wallpaper.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
-->

<set xmlns:android="http://schemas.android.com/apk/res/android"
android:detachWallpaper="true" android:shareInterpolator="false" android:startOffset="100">
android:detachWallpaper="true" android:shareInterpolator="false" >
<alpha
android:fromAlpha="0.0" android:toAlpha="1.0"
android:fillEnabled="true" android:fillBefore="true"
android:interpolator="@interpolator/decelerate_quint"
android:duration="400"/>
android:duration="300"/>

<translate android:fromYDelta="11%p" android:toYDelta="0"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
Expand Down
4 changes: 2 additions & 2 deletions core/res/res/anim/lock_screen_wallpaper_exit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
-->

<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false" android:startOffset="100">
android:shareInterpolator="false">
<alpha
android:fromAlpha="1.0" android:toAlpha="0.0"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
android:interpolator="@interpolator/fast_out_linear_in"
android:duration="150"/>
android:duration="200"/>

<!-- Empty animation so the animation has same duration as lock_screen_behind_enter animation
-->
Expand Down
2 changes: 2 additions & 0 deletions packages/SystemUI/src/com/android/systemui/Dependency.java
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,8 @@ public void start() {

mProviders.put(TunablePaddingService.class, () -> new TunablePaddingService());

mProviders.put(UiOffloadThread.class, UiOffloadThread::new);

// Put all dependencies above here so the factory can override them if it wants.
SystemUIFactory.getInstance().injectDependencies(mProviders, mContext);
}
Expand Down
34 changes: 34 additions & 0 deletions packages/SystemUI/src/com/android/systemui/UiOffloadThread.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/

package com.android.systemui;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
* Thread that offloads work from the UI thread but that is still perceptible to the user, so the
* priority is the same as the main thread.
*/
public class UiOffloadThread {

private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor();

public Future<?> submit(Runnable runnable) {
return mExecutorService.submit(runnable);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@
import android.view.MotionEvent;
import android.view.accessibility.AccessibilityManager;

import com.android.systemui.Dependency;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.analytics.DataCollector;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.statusbar.StatusBarState;

import java.io.PrintWriter;
Expand Down Expand Up @@ -63,6 +66,7 @@ public class FalsingManager implements SensorEventListener {
private final DataCollector mDataCollector;
private final HumanInteractionClassifier mHumanInteractionClassifier;
private final AccessibilityManager mAccessibilityManager;
private final UiOffloadThread mUiOffloadThread;

private static FalsingManager sInstance = null;

Expand All @@ -86,6 +90,7 @@ private FalsingManager(Context context) {
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mDataCollector = DataCollector.getInstance(mContext);
mHumanInteractionClassifier = HumanInteractionClassifier.getInstance(mContext);
mUiOffloadThread = Dependency.get(UiOffloadThread.class);
mScreenOn = context.getSystemService(PowerManager.class).isInteractive();

mContext.getContentResolver().registerContentObserver(
Expand Down Expand Up @@ -130,7 +135,11 @@ private boolean sessionEntrypoint() {
private void sessionExitpoint(boolean force) {
if (mSessionActive && (force || !shouldSessionBeActive())) {
mSessionActive = false;
mSensorManager.unregisterListener(this);

// This can be expensive, and doesn't need to happen on the main thread.
mUiOffloadThread.submit(() -> {
mSensorManager.unregisterListener(this);
});
}
}

Expand All @@ -154,7 +163,11 @@ private void registerSensors(int [] sensors) {
for (int sensorType : sensors) {
Sensor s = mSensorManager.getDefaultSensor(sensorType);
if (s != null) {
mSensorManager.registerListener(this, s, SensorManager.SENSOR_DELAY_GAME);

// This can be expensive, and doesn't need to happen on the main thread.
mUiOffloadThread.submit(() -> {
mSensorManager.registerListener(this, s, SensorManager.SENSOR_DELAY_GAME);
});
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
package com.android.systemui.keyguard;

import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.systemui.Dependency;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.misc.SystemServicesProxy;

import java.util.ArrayList;

Expand All @@ -26,21 +30,24 @@
public class DismissCallbackRegistry {

private final ArrayList<DismissCallbackWrapper> mDismissCallbacks = new ArrayList<>();
private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);

public void addCallback(IKeyguardDismissCallback callback) {
mDismissCallbacks.add(new DismissCallbackWrapper(callback));
}

public void notifyDismissCancelled() {
for (int i = mDismissCallbacks.size() - 1; i >= 0; i--) {
mDismissCallbacks.get(i).notifyDismissCancelled();
DismissCallbackWrapper callback = mDismissCallbacks.get(i);
mUiOffloadThread.submit(callback::notifyDismissCancelled);
}
mDismissCallbacks.clear();
}

public void notifyDismissSucceeded() {
for (int i = mDismissCallbacks.size() - 1; i >= 0; i--) {
mDismissCallbacks.get(i).notifyDismissSucceeded();
DismissCallbackWrapper callback = mDismissCallbacks.get(i);
mUiOffloadThread.submit(callback::notifyDismissSucceeded);
}
mDismissCallbacks.clear();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings.System;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.EventLog;
Expand All @@ -76,9 +77,13 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.LatencyTracker;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.Dependency;
import com.android.systemui.SystemUI;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.statusbar.phone.FingerprintUnlockController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.ScrimController;
Expand Down Expand Up @@ -195,6 +200,7 @@ public class KeyguardViewMediator extends SystemUI {
private AlarmManager mAlarmManager;
private AudioManager mAudioManager;
private StatusBarManager mStatusBarManager;
private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);

private boolean mSystemReady;
private boolean mBootCompleted;
Expand Down Expand Up @@ -1199,18 +1205,20 @@ private void updateInputRestricted() {
updateInputRestrictedLocked();
}
}

private void updateInputRestrictedLocked() {
boolean inputRestricted = isInputRestricted();
if (mInputRestricted != inputRestricted) {
mInputRestricted = inputRestricted;
int size = mKeyguardStateCallbacks.size();
for (int i = size - 1; i >= 0; i--) {
final IKeyguardStateCallback callback = mKeyguardStateCallbacks.get(i);
try {
mKeyguardStateCallbacks.get(i).onInputRestrictedStateChanged(inputRestricted);
callback.onInputRestrictedStateChanged(inputRestricted);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to call onDeviceProvisioned", e);
if (e instanceof DeadObjectException) {
mKeyguardStateCallbacks.remove(i);
mKeyguardStateCallbacks.remove(callback);
}
}
}
Expand Down Expand Up @@ -1569,9 +1577,11 @@ private void tryKeyguardDone() {
private void handleKeyguardDone() {
Trace.beginSection("KeyguardViewMediator#handleKeyguardDone");
final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
if (mLockPatternUtils.isSecure(currentUser)) {
mLockPatternUtils.getDevicePolicyManager().reportKeyguardDismissed(currentUser);
}
mUiOffloadThread.submit(() -> {
if (mLockPatternUtils.isSecure(currentUser)) {
mLockPatternUtils.getDevicePolicyManager().reportKeyguardDismissed(currentUser);
}
});
if (DEBUG) Log.d(TAG, "handleKeyguardDone");
synchronized (this) {
resetKeyguardDonePendingLocked();
Expand Down Expand Up @@ -1611,10 +1621,12 @@ private void sendUserPresentBroadcast() {
final UserHandle currentUser = new UserHandle(currentUserId);
final UserManager um = (UserManager) mContext.getSystemService(
Context.USER_SERVICE);
for (int profileId : um.getProfileIdsWithDisabled(currentUser.getIdentifier())) {
mContext.sendBroadcastAsUser(USER_PRESENT_INTENT, UserHandle.of(profileId));
}
getLockPatternUtils().userPresent(currentUserId);
mUiOffloadThread.submit(() -> {
for (int profileId : um.getProfileIdsWithDisabled(currentUser.getIdentifier())) {
mContext.sendBroadcastAsUser(USER_PRESENT_INTENT, UserHandle.of(profileId));
}
getLockPatternUtils().userPresent(currentUserId);
});
} else {
mBootSendUserPresent = true;
}
Expand Down Expand Up @@ -1659,25 +1671,32 @@ private void playSound(int soundId) {
if (mAudioManager == null) return;
mUiSoundsStreamType = mAudioManager.getUiSoundsStreamType();
}
// If the stream is muted, don't play the sound
if (mAudioManager.isStreamMute(mUiSoundsStreamType)) return;

mLockSoundStreamId = mLockSounds.play(soundId,
mLockSoundVolume, mLockSoundVolume, 1/*priortiy*/, 0/*loop*/, 1.0f/*rate*/);
mUiOffloadThread.submit(() -> {
// If the stream is muted, don't play the sound
if (mAudioManager.isStreamMute(mUiSoundsStreamType)) return;

int id = mLockSounds.play(soundId,
mLockSoundVolume, mLockSoundVolume, 1/*priortiy*/, 0/*loop*/, 1.0f/*rate*/);
synchronized (this) {
mLockSoundStreamId = id;
}
});

}
}

private void playTrustedSound() {
playSound(mTrustedSoundId);
}

private void updateActivityLockScreenState() {
Trace.beginSection("KeyguardViewMediator#updateActivityLockScreenState");
try {
ActivityManager.getService().setLockScreenShown(mShowing);
} catch (RemoteException e) {
}
Trace.endSection();
private void updateActivityLockScreenState(boolean showing) {
mUiOffloadThread.submit(() -> {
try {
ActivityManager.getService().setLockScreenShown(showing);
} catch (RemoteException e) {
}
});
}

/**
Expand Down Expand Up @@ -1846,7 +1865,10 @@ private void adjustStatusBarLocked() {
}

if (!(mContext instanceof Activity)) {
mStatusBarManager.disable(flags);
final int finalFlags = flags;
mUiOffloadThread.submit(() -> {
mStatusBarManager.disable(finalFlags);
});
}
}
}
Expand Down Expand Up @@ -2044,18 +2066,21 @@ private void setShowingLocked(boolean showing, boolean forceCallbacks) {
mShowing = showing;
int size = mKeyguardStateCallbacks.size();
for (int i = size - 1; i >= 0; i--) {
IKeyguardStateCallback callback = mKeyguardStateCallbacks.get(i);
try {
mKeyguardStateCallbacks.get(i).onShowingStateChanged(showing);
callback.onShowingStateChanged(showing);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to call onShowingStateChanged", e);
if (e instanceof DeadObjectException) {
mKeyguardStateCallbacks.remove(i);
mKeyguardStateCallbacks.remove(callback);
}
}
}
updateInputRestrictedLocked();
mTrustManager.reportKeyguardShowingChanged();
updateActivityLockScreenState();
mUiOffloadThread.submit(() -> {
mTrustManager.reportKeyguardShowingChanged();
});
updateActivityLockScreenState(showing);
}
}

Expand Down

0 comments on commit fabc743

Please sign in to comment.