summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/os/Looper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java77
-rw-r--r--services/core/java/com/android/server/am/UserController.java45
3 files changed, 102 insertions, 22 deletions
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index b58ff1fd4755..d299672ea1d5 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -147,7 +147,7 @@ public final class Looper {
}
final long traceTag = me.mTraceTag;
- if (traceTag != 0) {
+ if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 998f50f205d7..cb77d7b29c9f 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -31,6 +31,7 @@ import android.graphics.Region.Op;
import android.hardware.display.DisplayManager;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Message;
import android.util.AttributeSet;
import android.view.Display;
import android.view.DisplayInfo;
@@ -106,6 +107,11 @@ public class DividerView extends FrameLayout implements OnTouchListener,
private static final Interpolator IME_ADJUST_INTERPOLATOR =
new PathInterpolator(0.2f, 0f, 0.1f, 1f);
+ private static final long ONE_MS_IN_NS = 1000000;
+ private static final long ONE_S_IN_NS = ONE_MS_IN_NS * 1000;
+
+ private static final int MSG_RESIZE_STACK = 0;
+
private DividerHandleView mHandle;
private View mBackground;
private MinimizedDockShadow mMinimizedShadow;
@@ -150,7 +156,25 @@ public class DividerView extends FrameLayout implements OnTouchListener,
private boolean mDockedStackMinimized;
private boolean mAdjustedForIme;
private DividerState mState;
- private final Handler mHandler = new Handler();
+
+ /**
+ * The offset between vsync-app and vsync-surfaceflinger. See
+ * {@link #calculateAppSurfaceFlingerVsyncOffsetMs} why this is necessary.
+ */
+ private long mSurfaceFlingerOffsetMs;
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_RESIZE_STACK:
+ resizeStack(msg.arg1, msg.arg2, (SnapTarget) msg.obj);
+ break;
+ default:
+ super.handleMessage(msg);
+ }
+ }
+ };
private final AccessibilityDelegate mHandleDelegate = new AccessibilityDelegate() {
@Override
@@ -290,6 +314,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
protected void onAttachedToWindow() {
super.onAttachedToWindow();
EventBus.getDefault().register(this);
+ mSurfaceFlingerOffsetMs = calculateAppSurfaceFlingerVsyncOffsetMs();
}
@Override
@@ -298,6 +323,25 @@ public class DividerView extends FrameLayout implements OnTouchListener,
EventBus.getDefault().unregister(this);
}
+ /**
+ * This method calculates the offset between vsync-surfaceflinger and vsync-app. If vsync-app
+ * is a couple of milliseconds before vsync-sf, a touch or animation event that causes the
+ * stacks to be resized are sometimes processed before the vsync-sf tick, and sometimes after,
+ * which leads to jank. Figure out this difference here and then post all the touch/animation
+ * events to start being processed at vsync-sf.
+ *
+ * @return The offset between vsync-app and vsync-sf, or 0 if vsync app happens after vsync-sf.
+ */
+ private long calculateAppSurfaceFlingerVsyncOffsetMs() {
+ Display display = getDisplay();
+
+ // Calculate vsync offset from SurfaceFlinger.
+ // See frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp:getDisplayConfigs
+ long vsyncPeriod = (long) (ONE_S_IN_NS / display.getRefreshRate());
+ long sfVsyncOffset = vsyncPeriod - (display.getPresentationDeadlineNanos() - ONE_MS_IN_NS);
+ return Math.max(0, (sfVsyncOffset - display.getAppVsyncOffsetNanos()) / ONE_MS_IN_NS);
+ }
+
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
if (mStableInsets.left != insets.getStableInsetLeft()
@@ -453,7 +497,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
if (mMoving && mDockSide != WindowManager.DOCKED_INVALID) {
SnapTarget snapTarget = mSnapAlgorithm.calculateSnapTarget(
mStartPosition, 0 /* velocity */, false /* hardDismiss */);
- resizeStack(calculatePosition(x, y), mStartPosition, snapTarget);
+ resizeStackDelayed(calculatePosition(x, y), mStartPosition, snapTarget);
}
break;
case MotionEvent.ACTION_UP:
@@ -532,10 +576,11 @@ public class DividerView extends FrameLayout implements OnTouchListener,
final long endDelay) {
final boolean taskPositionSameAtEnd = snapTarget.flag == SnapTarget.FLAG_NONE;
ValueAnimator anim = ValueAnimator.ofInt(position, snapTarget.position);
- anim.addUpdateListener(animation -> resizeStack((Integer) animation.getAnimatedValue(),
+ anim.addUpdateListener(animation -> resizeStackDelayed((int) animation.getAnimatedValue(),
taskPositionSameAtEnd && animation.getAnimatedFraction() == 1f
? TASK_POSITION_SAME
- : snapTarget.taskPosition, snapTarget));
+ : snapTarget.taskPosition,
+ snapTarget));
Runnable endAction = () -> {
commitSnapFlags(snapTarget);
mWindowManagerProxy.setResizing(false);
@@ -551,15 +596,24 @@ public class DividerView extends FrameLayout implements OnTouchListener,
@Override
public void onAnimationCancel(Animator animation) {
+ mHandler.removeMessages(MSG_RESIZE_STACK);
mCancelled = true;
}
@Override
public void onAnimationEnd(Animator animation) {
- if (endDelay == 0 || mCancelled) {
+ long delay = 0;
+ if (endDelay != 0) {
+ delay = endDelay;
+ } else if (mCancelled) {
+ delay = 0;
+ } else if (mSurfaceFlingerOffsetMs != 0) {
+ delay = mSurfaceFlingerOffsetMs;
+ }
+ if (delay == 0) {
endAction.run();
} else {
- mHandler.postDelayed(endAction, endDelay);
+ mHandler.postDelayed(endAction, delay);
}
}
});
@@ -793,6 +847,17 @@ public class DividerView extends FrameLayout implements OnTouchListener,
mDisplayHeight, mDividerSize);
}
+ public void resizeStackDelayed(int position, int taskPosition, SnapTarget taskSnapTarget) {
+ if (mSurfaceFlingerOffsetMs != 0) {
+ Message message = mHandler.obtainMessage(MSG_RESIZE_STACK, position, taskPosition,
+ taskSnapTarget);
+ message.setAsynchronous(true);
+ mHandler.sendMessageDelayed(message, mSurfaceFlingerOffsetMs);
+ } else {
+ resizeStack(position, taskPosition, taskSnapTarget);
+ }
+ }
+
public void resizeStack(int position, int taskPosition, SnapTarget taskSnapTarget) {
calculateBoundsForPosition(position, mDockSide, mDockedRect);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index c9283b2283b9..4bc148bef290 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -279,6 +279,7 @@ final class UserController {
*/
private void finishUserUnlocking(final UserState uss) {
final int userId = uss.mHandle.getIdentifier();
+ boolean proceedWithUnlock = false;
synchronized (mService) {
// Bail if we ended up with a stale user
if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return;
@@ -288,20 +289,24 @@ final class UserController {
if (uss.setState(STATE_RUNNING_LOCKED, STATE_RUNNING_UNLOCKING)) {
getUserManagerInternal().setUserState(userId, uss.state);
- uss.mUnlockProgress.start();
-
- // Prepare app storage before we go any further
- uss.mUnlockProgress.setProgress(5,
- mService.mContext.getString(R.string.android_start_title));
- mUserManager.onBeforeUnlockUser(userId);
- uss.mUnlockProgress.setProgress(20);
-
- // Dispatch unlocked to system services; when fully dispatched,
- // that calls through to the next "unlocked" phase
- mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss)
- .sendToTarget();
+ proceedWithUnlock = true;
}
}
+
+ if (proceedWithUnlock) {
+ uss.mUnlockProgress.start();
+
+ // Prepare app storage before we go any further
+ uss.mUnlockProgress.setProgress(5,
+ mService.mContext.getString(R.string.android_start_title));
+ mUserManager.onBeforeUnlockUser(userId);
+ uss.mUnlockProgress.setProgress(20);
+
+ // Dispatch unlocked to system services; when fully dispatched,
+ // that calls through to the next "unlocked" phase
+ mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss)
+ .sendToTarget();
+ }
}
/**
@@ -962,6 +967,7 @@ final class UserController {
boolean unlockUserCleared(final int userId, byte[] token, byte[] secret,
IProgressListener listener) {
+ UserState uss;
synchronized (mService) {
// TODO Move this block outside of synchronized if it causes lock contention
if (!StorageManager.isUserKeyUnlocked(userId)) {
@@ -976,7 +982,7 @@ final class UserController {
}
// Bail if user isn't actually running, otherwise register the given
// listener to watch for unlock progress
- final UserState uss = mStartedUsers.get(userId);
+ uss = mStartedUsers.get(userId);
if (uss == null) {
notifyFinished(userId, listener);
return false;
@@ -984,8 +990,12 @@ final class UserController {
uss.mUnlockProgress.addListener(listener);
uss.tokenProvided = (token != null);
}
+ }
- finishUserUnlocking(uss);
+ finishUserUnlocking(uss);
+
+ final ArraySet<Integer> childProfilesToUnlock = new ArraySet<>();
+ synchronized (mService) {
// We just unlocked a user, so let's now attempt to unlock any
// managed profiles under that user.
@@ -995,11 +1005,16 @@ final class UserController {
if (parent != null && parent.id == userId && testUserId != userId) {
Slog.d(TAG, "User " + testUserId + " (parent " + parent.id
+ "): attempting unlock because parent was just unlocked");
- maybeUnlockUser(testUserId);
+ childProfilesToUnlock.add(testUserId);
}
}
}
+ final int size = childProfilesToUnlock.size();
+ for (int i = 0; i < size; i++) {
+ maybeUnlockUser(childProfilesToUnlock.valueAt(i));
+ }
+
return true;
}