diff options
94 files changed, 2629 insertions, 1734 deletions
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java index 877e7d3b3bf7..6f4bb456c478 100644 --- a/core/java/android/app/ActivityTransitionState.java +++ b/core/java/android/app/ActivityTransitionState.java @@ -145,7 +145,10 @@ class ActivityTransitionState { * that it is preserved through activty destroy and restore. */ private ArrayList<String> getPendingExitNames() { - if (mPendingExitNames == null && mEnterTransitionCoordinator != null) { + if (mPendingExitNames == null + && mEnterTransitionCoordinator != null + && !mEnterTransitionCoordinator.isReturning() + ) { mPendingExitNames = mEnterTransitionCoordinator.getPendingExitSharedElementNames(); } return mPendingExitNames; @@ -202,6 +205,7 @@ class ActivityTransitionState { restoreExitedViews(); activity.getWindow().getDecorView().setVisibility(View.VISIBLE); } + getPendingExitNames(); // Set mPendingExitNames before resetting mEnterTransitionCoordinator mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity, resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning(), mEnterActivityOptions.isCrossTask()); @@ -250,6 +254,7 @@ class ActivityTransitionState { public void onStop(Activity activity) { restoreExitedViews(); if (mEnterTransitionCoordinator != null) { + getPendingExitNames(); // Set mPendingExitNames before clearing mEnterTransitionCoordinator.stop(); mEnterTransitionCoordinator = null; } @@ -275,6 +280,7 @@ class ActivityTransitionState { restoreReenteringViews(); } else if (mEnterTransitionCoordinator.isReturning()) { mEnterTransitionCoordinator.runAfterTransitionsComplete(() -> { + getPendingExitNames(); // Set mPendingExitNames before clearing mEnterTransitionCoordinator = null; }); } @@ -374,6 +380,7 @@ class ActivityTransitionState { } public void startExitOutTransition(Activity activity, Bundle options) { + getPendingExitNames(); // Set mPendingExitNames before clearing mEnterTransitionCoordinator mEnterTransitionCoordinator = null; if (!activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS) || mExitTransitionCoordinators == null) { diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 65265da1038a..ab11d6a3e562 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -15423,7 +15423,7 @@ public class DevicePolicyManager { * * @see #setWifiSsidPolicy(WifiSsidPolicy) * @throws SecurityException if the caller is not a device owner or a profile owner on - * an organization-owned managed profile or a system app. + * an organization-owned managed profile. */ @Nullable public WifiSsidPolicy getWifiSsidPolicy() { diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index 18d86d69206f..1c4898a4c8d0 100644 --- a/core/java/android/hardware/SystemSensorManager.java +++ b/core/java/android/hardware/SystemSensorManager.java @@ -882,6 +882,7 @@ public class SystemSensorManager extends SensorManager { } // Indicate if the discontinuity count changed + t.firstEventAfterDiscontinuity = false; if (t.sensor.getType() == Sensor.TYPE_HEAD_TRACKER) { final int lastCount = mSensorDiscontinuityCounts.get(handle); final int curCount = Float.floatToIntBits(values[6]); diff --git a/core/java/android/service/games/TEST_MAPPING b/core/java/android/service/games/TEST_MAPPING new file mode 100644 index 000000000000..3e551ef6bb54 --- /dev/null +++ b/core/java/android/service/games/TEST_MAPPING @@ -0,0 +1,16 @@ +{ + "presubmit": [ + // TODO(b/245615658): fix flaky CTS test CtsGameServiceTestCases and add it as presubmit + { + "name": "FrameworksMockingServicesTests", + "options": [ + { + "include-filter": "android.service.games" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] + } + ] +}
\ No newline at end of file diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java index ad4f9f718f7f..684d5665c862 100644 --- a/core/java/android/service/trust/TrustAgentService.java +++ b/core/java/android/service/trust/TrustAgentService.java @@ -141,7 +141,9 @@ public class TrustAgentService extends Service { * * Without this flag, the message passed to {@code grantTrust} is only used for debugging * purposes. With the flag, it may be displayed to the user as the reason why the device is - * unlocked. + * unlocked. If this flag isn't set OR the message is set to null, the device will display + * its own default message for trust granted. If the TrustAgent intentionally doesn't want to + * show any message, then it can set this flag AND set the message to an empty string. */ public static final int FLAG_GRANT_TRUST_DISPLAY_MESSAGE = 1 << 3; diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 586c1935bbef..7acf319a5049 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -107,14 +107,6 @@ import java.util.function.Consumer; * and scaling a SurfaceView on screen will not cause rendering artifacts. Such * artifacts may occur on previous versions of the platform when its window is * positioned asynchronously.</p> - * - * <p class="note"><strong>Note:</strong> Starting in platform version - * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, SurfaceView will support arbitrary - * alpha blending. Prior platform versions ignored alpha values on the SurfaceView if they were - * between 0 and 1. If the SurfaceView is configured with Z-above, then the alpha is applied - * directly to the Surface. If the SurfaceView is configured with Z-below, then the alpha is - * applied to the hole punch directly. Note that when using Z-below, overlapping SurfaceViews - * may not blend properly as a consequence of not applying alpha to the surface content directly. */ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCallback { private static final String TAG = "SurfaceView"; @@ -154,7 +146,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall Paint mRoundedViewportPaint; int mSubLayer = APPLICATION_MEDIA_SUBLAYER; - int mRequestedSubLayer = APPLICATION_MEDIA_SUBLAYER; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) boolean mIsCreating = false; @@ -186,7 +177,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall @UnsupportedAppUsage int mRequestedFormat = PixelFormat.RGB_565; - float mAlpha = 1f; + boolean mUseAlpha = false; + float mSurfaceAlpha = 1f; boolean mClipSurfaceToBounds; int mBackgroundColor = Color.BLACK; @@ -343,25 +335,58 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall * @hide */ public void setUseAlpha() { - // TODO(b/241474646): Remove me - return; + if (!mUseAlpha) { + mUseAlpha = true; + updateSurfaceAlpha(); + } } @Override public void setAlpha(float alpha) { + // Sets the opacity of the view to a value, where 0 means the view is completely transparent + // and 1 means the view is completely opaque. + // + // Note: Alpha value of this view is ignored by default. To enable alpha blending, you need + // to call setUseAlpha() as well. + // This view doesn't support translucent opacity if the view is located z-below, since the + // logic to punch a hole in the view hierarchy cannot handle such case. See also + // #clearSurfaceViewPort(Canvas) if (DEBUG) { Log.d(TAG, System.identityHashCode(this) - + " setAlpha: alpha=" + alpha); + + " setAlpha: mUseAlpha = " + mUseAlpha + " alpha=" + alpha); } super.setAlpha(alpha); + updateSurfaceAlpha(); } - @Override - protected boolean onSetAlpha(int alpha) { - if (Math.round(mAlpha * 255) != alpha) { - updateSurface(); + private float getFixedAlpha() { + // Compute alpha value to be set on the underlying surface. + final float alpha = getAlpha(); + return mUseAlpha && (mSubLayer > 0 || alpha == 0f) ? alpha : 1f; + } + + private void updateSurfaceAlpha() { + if (!mUseAlpha || !mHaveFrame || mSurfaceControl == null) { + return; + } + final float viewAlpha = getAlpha(); + if (mSubLayer < 0 && 0f < viewAlpha && viewAlpha < 1f) { + Log.w(TAG, System.identityHashCode(this) + + " updateSurfaceAlpha:" + + " translucent color is not supported for a surface placed z-below."); + } + final ViewRootImpl viewRoot = getViewRootImpl(); + if (viewRoot == null) { + return; + } + final float alpha = getFixedAlpha(); + if (alpha != mSurfaceAlpha) { + final Transaction transaction = new Transaction(); + transaction.setAlpha(mSurfaceControl, alpha); + viewRoot.applyTransactionOnDraw(transaction); + damageInParent(); + mSurfaceAlpha = alpha; } - return true; } private void performDrawFinished() { @@ -509,15 +534,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall invalidate(); } - @Override - public boolean hasOverlappingRendering() { - // SurfaceViews only alpha composite by modulating the Surface alpha for Z-above, or - // applying alpha on the hole punch for Z-below - no deferral to a layer is necessary. - return false; - } - private void clearSurfaceViewPort(Canvas canvas) { - final float alpha = getAlpha(); if (mCornerRadius > 0f) { canvas.getClipBounds(mTmpRect); if (mClipSurfaceToBounds && mClipBounds != null) { @@ -529,11 +546,10 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mTmpRect.right, mTmpRect.bottom, mCornerRadius, - mCornerRadius, - alpha + mCornerRadius ); } else { - canvas.punchHole(0f, 0f, getWidth(), getHeight(), 0f, 0f, alpha); + canvas.punchHole(0f, 0f, getWidth(), getHeight(), 0f, 0f); } } @@ -636,10 +652,10 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } else { subLayer = APPLICATION_MEDIA_SUBLAYER; } - if (mRequestedSubLayer == subLayer) { + if (mSubLayer == subLayer) { return false; } - mRequestedSubLayer = subLayer; + mSubLayer = subLayer; if (!allowDynamicChange) { return false; @@ -651,8 +667,9 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall if (viewRoot == null) { return true; } - - updateSurface(); + final Transaction transaction = new SurfaceControl.Transaction(); + updateRelativeZ(transaction); + viewRoot.applyTransactionOnDraw(transaction); invalidate(); return true; } @@ -705,7 +722,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } private void releaseSurfaces(boolean releaseSurfacePackage) { - mAlpha = 1f; + mSurfaceAlpha = 1f; + synchronized (mSurfaceControlLock) { mSurface.destroy(); if (mBlastBufferQueue != null) { @@ -752,7 +770,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } private boolean performSurfaceTransaction(ViewRootImpl viewRoot, Translator translator, - boolean creating, boolean sizeChanged, boolean hintChanged, boolean relativeZChanged, + boolean creating, boolean sizeChanged, boolean hintChanged, Transaction surfaceUpdateTransaction) { boolean realSizeChanged = false; @@ -782,20 +800,14 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall surfaceUpdateTransaction.hide(mSurfaceControl); } + + updateBackgroundVisibility(surfaceUpdateTransaction); updateBackgroundColor(surfaceUpdateTransaction); - if (isAboveParent()) { - float alpha = getAlpha(); + if (mUseAlpha) { + float alpha = getFixedAlpha(); surfaceUpdateTransaction.setAlpha(mSurfaceControl, alpha); - } - - if (relativeZChanged) { - if (!isAboveParent()) { - // If we're moving from z-above to z-below, then restore the surface alpha back to 1 - // and let the holepunch drive visibility and blending. - surfaceUpdateTransaction.setAlpha(mSurfaceControl, 1.f); - } - updateRelativeZ(surfaceUpdateTransaction); + mSurfaceAlpha = alpha; } surfaceUpdateTransaction.setCornerRadius(mSurfaceControl, mCornerRadius); @@ -861,7 +873,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } finally { mSurfaceLock.unlock(); } - return realSizeChanged; } @@ -895,10 +906,10 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall int myHeight = mRequestedHeight; if (myHeight <= 0) myHeight = getHeight(); - final float alpha = getAlpha(); + final float alpha = getFixedAlpha(); final boolean formatChanged = mFormat != mRequestedFormat; final boolean visibleChanged = mVisible != mRequestedVisible; - final boolean alphaChanged = mAlpha != alpha; + final boolean alphaChanged = mSurfaceAlpha != alpha; final boolean creating = (mSurfaceControl == null || formatChanged || visibleChanged) && mRequestedVisible; final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight; @@ -910,17 +921,17 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall || getHeight() != mScreenRect.height(); final boolean hintChanged = (viewRoot.getBufferTransformHint() != mTransformHint) && mRequestedVisible; - final boolean relativeZChanged = mSubLayer != mRequestedSubLayer; - if (creating || formatChanged || sizeChanged || visibleChanged - || alphaChanged || windowVisibleChanged || positionChanged - || layoutSizeChanged || hintChanged || relativeZChanged) { + if (creating || formatChanged || sizeChanged || visibleChanged || + (mUseAlpha && alphaChanged) || windowVisibleChanged || + positionChanged || layoutSizeChanged || hintChanged) { if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Changes: creating=" + creating + " format=" + formatChanged + " size=" + sizeChanged + " visible=" + visibleChanged + " alpha=" + alphaChanged + " hint=" + hintChanged + + " mUseAlpha=" + mUseAlpha + " visible=" + visibleChanged + " left=" + (mWindowSpaceLeft != mLocation[0]) + " top=" + (mWindowSpaceTop != mLocation[1])); @@ -932,10 +943,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mSurfaceWidth = myWidth; mSurfaceHeight = myHeight; mFormat = mRequestedFormat; - mAlpha = alpha; mLastWindowVisibility = mWindowVisibility; mTransformHint = viewRoot.getBufferTransformHint(); - mSubLayer = mRequestedSubLayer; mScreenRect.left = mWindowSpaceLeft; mScreenRect.top = mWindowSpaceTop; @@ -959,7 +968,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } final boolean redrawNeeded = sizeChanged || creating || hintChanged - || (mVisible && !mDrawFinished) || alphaChanged || relativeZChanged; + || (mVisible && !mDrawFinished); boolean shouldSyncBuffer = redrawNeeded && viewRoot.wasRelayoutRequested() && viewRoot.isInLocalSync(); SyncBufferTransactionCallback syncBufferTransactionCallback = null; @@ -970,9 +979,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall syncBufferTransactionCallback::onTransactionReady); } - final boolean realSizeChanged = performSurfaceTransaction(viewRoot, translator, - creating, sizeChanged, hintChanged, relativeZChanged, - surfaceUpdateTransaction); + final boolean realSizeChanged = performSurfaceTransaction(viewRoot, + translator, creating, sizeChanged, hintChanged, surfaceUpdateTransaction); try { SurfaceHolder.Callback[] callbacks = null; @@ -1614,8 +1622,11 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall */ @Override public void unlockCanvasAndPost(Canvas canvas) { - mSurface.unlockCanvasAndPost(canvas); - mSurfaceLock.unlock(); + try { + mSurface.unlockCanvasAndPost(canvas); + } finally { + mSurfaceLock.unlock(); + } } @Override diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index ffae3df158dd..8fee4db458b3 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -12890,7 +12890,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (mViewTranslationCallback != null) { mViewTranslationCallback.onClearTranslation(this); } - clearViewTranslationCallback(); clearViewTranslationResponse(); if (hasTranslationTransientState()) { setHasTransientState(false); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index a49caafc63f1..8656af2151d9 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -285,12 +285,18 @@ public interface WindowManager extends ViewManager { int TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER = 21; /** - * Keyguard is being occluded. + * Keyguard is being occluded by non-Dream. * @hide */ int TRANSIT_OLD_KEYGUARD_OCCLUDE = 22; /** + * Keyguard is being occluded by Dream. + * @hide + */ + int TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM = 33; + + /** * Keyguard is being unoccluded. * @hide */ diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java index 7001c69f4042..f097bf73eb76 100644 --- a/core/java/com/android/internal/os/BatteryStatsHistory.java +++ b/core/java/com/android/internal/os/BatteryStatsHistory.java @@ -16,17 +16,23 @@ package com.android.internal.os; +import android.annotation.NonNull; import android.annotation.Nullable; import android.os.BatteryManager; +import android.os.BatteryStats; +import android.os.BatteryStats.BitDescription; import android.os.BatteryStats.HistoryItem; import android.os.BatteryStats.HistoryStepDetails; import android.os.BatteryStats.HistoryTag; import android.os.BatteryStats.MeasuredEnergyDetails; +import android.os.Build; import android.os.Parcel; import android.os.ParcelFormatException; import android.os.Process; import android.os.StatFs; import android.os.SystemClock; +import android.os.SystemProperties; +import android.os.Trace; import android.util.ArraySet; import android.util.AtomicFile; import android.util.Slog; @@ -210,6 +216,42 @@ public class BatteryStatsHistory { } /** + * A delegate for android.os.Trace to allow testing static calls. Due to + * limitations in Android Tracing (b/153319140), the delegate also records + * counter values in system properties which allows reading the value at the + * start of a tracing session. This overhead is limited to userdebug builds. + * On user builds, tracing still occurs but the counter value will be missing + * until the first change occurs. + */ + @VisibleForTesting + public static class TraceDelegate { + // Note: certain tests currently run as platform_app which is not allowed + // to set debug system properties. To ensure that system properties are set + // only when allowed, we check the current UID. + private final boolean mShouldSetProperty = + Build.IS_USERDEBUG && (Process.myUid() == Process.SYSTEM_UID); + + /** + * Returns true if trace counters should be recorded. + */ + public boolean tracingEnabled() { + return Trace.isTagEnabled(Trace.TRACE_TAG_POWER) || mShouldSetProperty; + } + + /** + * Records the counter value with the given name. + */ + public void traceCounter(@NonNull String name, int value) { + Trace.traceCounter(Trace.TRACE_TAG_POWER, name, value); + if (mShouldSetProperty) { + SystemProperties.set("debug.tracing." + name, Integer.toString(value)); + } + } + } + + private TraceDelegate mTracer; + + /** * Constructor * * @param systemDir typically /data/system @@ -219,19 +261,20 @@ public class BatteryStatsHistory { public BatteryStatsHistory(File systemDir, int maxHistoryFiles, int maxHistoryBufferSize, HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) { this(Parcel.obtain(), systemDir, maxHistoryFiles, maxHistoryBufferSize, - stepDetailsCalculator, clock); + stepDetailsCalculator, clock, new TraceDelegate()); initHistoryBuffer(); } @VisibleForTesting public BatteryStatsHistory(Parcel historyBuffer, File systemDir, int maxHistoryFiles, int maxHistoryBufferSize, - HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) { + HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, TraceDelegate tracer) { mHistoryBuffer = historyBuffer; mSystemDir = systemDir; mMaxHistoryFiles = maxHistoryFiles; mMaxHistoryBufferSize = maxHistoryBufferSize; mStepDetailsCalculator = stepDetailsCalculator; + mTracer = tracer; mClock = clock; mHistoryDir = new File(systemDir, HISTORY_DIR); @@ -272,6 +315,7 @@ public class BatteryStatsHistory { public BatteryStatsHistory(HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) { mStepDetailsCalculator = stepDetailsCalculator; + mTracer = new TraceDelegate(); mClock = clock; mHistoryBuffer = Parcel.obtain(); @@ -287,6 +331,7 @@ public class BatteryStatsHistory { private BatteryStatsHistory(Parcel historyBuffer, HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) { mHistoryBuffer = historyBuffer; + mTracer = new TraceDelegate(); mClock = clock; mSystemDir = null; mHistoryDir = null; @@ -338,7 +383,7 @@ public class BatteryStatsHistory { // Make a copy of battery history to avoid concurrent modification. Parcel historyBuffer = Parcel.obtain(); historyBuffer.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize()); - return new BatteryStatsHistory(historyBuffer, mSystemDir, 0, 0, null, null); + return new BatteryStatsHistory(historyBuffer, mSystemDir, 0, 0, null, null, mTracer); } /** @@ -1120,6 +1165,30 @@ public class BatteryStatsHistory { } /** + * Writes changes to a HistoryItem state bitmap to Atrace. + */ + private void recordTraceCounters(int oldval, int newval, BitDescription[] descriptions) { + if (!mTracer.tracingEnabled()) return; + + int diff = oldval ^ newval; + if (diff == 0) return; + + for (int i = 0; i < descriptions.length; i++) { + BitDescription bd = descriptions[i]; + if ((diff & bd.mask) == 0) continue; + + int value; + if (bd.shift < 0) { + value = (newval & bd.mask) != 0 ? 1 : 0; + } else { + value = (newval & bd.mask) >> bd.shift; + } + + mTracer.traceCounter("battery_stats." + bd.name, value); + } + } + + /** * Writes the current history item to history. */ public void writeHistoryItem(long elapsedRealtimeMs, long uptimeMs) { @@ -1159,6 +1228,12 @@ public class BatteryStatsHistory { + Integer.toHexString(diffStates2) + " lastDiff2=" + Integer.toHexString(lastDiffStates2)); } + + recordTraceCounters(mHistoryLastWritten.states, + cur.states & mActiveHistoryStates, BatteryStats.HISTORY_STATE_DESCRIPTIONS); + recordTraceCounters(mHistoryLastWritten.states2, + cur.states2 & mActiveHistoryStates2, BatteryStats.HISTORY_STATE2_DESCRIPTIONS); + if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE && timeDiffMs < 1000 && (diffStates & lastDiffStates) == 0 && (diffStates2 & lastDiffStates2) == 0 diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java index 9f21760a49e0..4cf0ba19a887 100644 --- a/core/java/com/android/internal/widget/ConversationLayout.java +++ b/core/java/com/android/internal/widget/ConversationLayout.java @@ -111,6 +111,7 @@ public class ConversationLayout extends FrameLayout private Icon mLargeIcon; private View mExpandButtonContainer; private ViewGroup mExpandButtonAndContentContainer; + private ViewGroup mExpandButtonContainerA11yContainer; private NotificationExpandButton mExpandButton; private MessagingLinearLayout mImageMessageContainer; private int mBadgeProtrusion; @@ -234,6 +235,8 @@ public class ConversationLayout extends FrameLayout }); mConversationText = findViewById(R.id.conversation_text); mExpandButtonContainer = findViewById(R.id.expand_button_container); + mExpandButtonContainerA11yContainer = + findViewById(R.id.expand_button_a11y_container); mConversationHeader = findViewById(R.id.conversation_header); mContentContainer = findViewById(R.id.notification_action_list_margin_target); mExpandButtonAndContentContainer = findViewById(R.id.expand_button_and_content_container); @@ -1091,7 +1094,7 @@ public class ConversationLayout extends FrameLayout newContainer = mExpandButtonAndContentContainer; } else { buttonGravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP; - newContainer = this; + newContainer = mExpandButtonContainerA11yContainer; } mExpandButton.setExpanded(!mIsCollapsed); diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml index 42fb4a26dd3b..ce8a90495572 100644 --- a/core/res/res/layout/notification_template_material_conversation.xml +++ b/core/res/res/layout/notification_template_material_conversation.xml @@ -89,45 +89,62 @@ <include layout="@layout/notification_material_action_list" /> </com.android.internal.widget.RemeasuringLinearLayout> - <!--This is dynamically placed between here and at the end of the layout. It starts here since - only FrameLayout layout params have gravity--> + <!--expand_button_a11y_container ensures talkback focus order is correct when view is expanded. + The -1px of marginTop and 1px of paddingTop make sure expand_button_a11y_container is prior to + its sibling view in accessibility focus order. + {see android.view.ViewGroup.addChildrenForAccessibility()} + expand_button_container will be moved under expand_button_and_content_container when collapsed, + this dynamic movement ensures message can flow under expand button when expanded--> <FrameLayout - android:id="@+id/expand_button_container" - android:layout_width="wrap_content" + android:id="@+id/expand_button_a11y_container" + android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="end|top" android:clipChildren="false" - android:clipToPadding="false"> - <!--This layout makes sure that we can nicely center the expand content in the - collapsed layout while the parent makes sure that we're never laid out bigger - than the messaging content.--> - <LinearLayout - android:id="@+id/expand_button_touch_container" + android:clipToPadding="false" + android:layout_marginTop="-1px" + android:paddingTop="1px" + > + <!--expand_button_container is dynamically placed between here and at the end of the + layout. It starts here since only FrameLayout layout params have gravity--> + <FrameLayout + android:id="@+id/expand_button_container" android:layout_width="wrap_content" - android:layout_height="@dimen/conversation_expand_button_height" - android:orientation="horizontal" + android:layout_height="match_parent" android:layout_gravity="end|top" - android:paddingEnd="0dp" - android:clipToPadding="false" android:clipChildren="false" - > - <!-- Images --> - <com.android.internal.widget.MessagingLinearLayout - android:id="@+id/conversation_image_message_container" - android:forceHasOverlappingRendering="false" - android:layout_width="40dp" - android:layout_height="40dp" - android:layout_marginStart="@dimen/conversation_image_start_margin" - android:spacing="0dp" - android:layout_gravity="center" + android:clipToPadding="false"> + <!--expand_button_touch_container makes sure that we can nicely center the expand + content in the collapsed layout while the parent makes sure that we're never laid out + bigger than the messaging content.--> + <LinearLayout + android:id="@+id/expand_button_touch_container" + android:layout_width="wrap_content" + android:layout_height="@dimen/conversation_expand_button_height" + android:orientation="horizontal" + android:layout_gravity="end|top" + android:paddingEnd="0dp" android:clipToPadding="false" android:clipChildren="false" - /> - <include layout="@layout/notification_expand_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - /> - </LinearLayout> + > + <!-- Images --> + <com.android.internal.widget.MessagingLinearLayout + android:id="@+id/conversation_image_message_container" + android:forceHasOverlappingRendering="false" + android:layout_width="40dp" + android:layout_height="40dp" + android:layout_marginStart="@dimen/conversation_image_start_margin" + android:spacing="0dp" + android:layout_gravity="center" + android:clipToPadding="false" + android:clipChildren="false" + /> + <include layout="@layout/notification_expand_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + /> + </LinearLayout> + </FrameLayout> </FrameLayout> </com.android.internal.widget.ConversationLayout> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index e5db96a034f7..069bfe78fa05 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4288,6 +4288,7 @@ <java-symbol type="id" name="conversation_icon_badge_ring" /> <java-symbol type="id" name="conversation_icon_badge_bg" /> <java-symbol type="id" name="expand_button_container" /> + <java-symbol type="id" name="expand_button_a11y_container" /> <java-symbol type="id" name="expand_button_touch_container" /> <java-symbol type="id" name="messaging_group_content_container" /> <java-symbol type="id" name="expand_button_and_content_container" /> diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java index 54d64280c6f7..a8ab6d9e494e 100644 --- a/graphics/java/android/graphics/BaseCanvas.java +++ b/graphics/java/android/graphics/BaseCanvas.java @@ -670,9 +670,8 @@ public abstract class BaseCanvas { /** * @hide */ - public void punchHole(float left, float top, float right, float bottom, float rx, float ry, - float alpha) { - nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry, alpha); + public void punchHole(float left, float top, float right, float bottom, float rx, float ry) { + nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry); } /** @@ -824,5 +823,5 @@ public abstract class BaseCanvas { float hOffset, float vOffset, int flags, long nativePaint); private static native void nPunchHole(long renderer, float left, float top, float right, - float bottom, float rx, float ry, float alpha); + float bottom, float rx, float ry); } diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java index 1ba79b87e87c..d06f665631cc 100644 --- a/graphics/java/android/graphics/BaseRecordingCanvas.java +++ b/graphics/java/android/graphics/BaseRecordingCanvas.java @@ -610,9 +610,8 @@ public class BaseRecordingCanvas extends Canvas { * @hide */ @Override - public void punchHole(float left, float top, float right, float bottom, float rx, float ry, - float alpha) { - nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry, alpha); + public void punchHole(float left, float top, float right, float bottom, float rx, float ry) { + nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry); } @FastNative @@ -743,5 +742,5 @@ public class BaseRecordingCanvas extends Canvas { @FastNative private static native void nPunchHole(long renderer, float left, float top, float right, - float bottom, float rx, float ry, float alpha); + float bottom, float rx, float ry); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 7c50982b7b86..347904e67f3e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -82,7 +82,7 @@ import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; -import com.android.wm.shell.transition.SplitscreenPipMixedHandler; +import com.android.wm.shell.transition.DefaultMixedHandler; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.unfold.ShellUnfoldProgressProvider; import com.android.wm.shell.unfold.UnfoldAnimationController; @@ -484,13 +484,13 @@ public abstract class WMShellModule { @WMSingleton @Provides - static SplitscreenPipMixedHandler provideSplitscreenPipMixedHandler( + static DefaultMixedHandler provideDefaultMixedHandler( ShellInit shellInit, Optional<SplitScreenController> splitScreenOptional, Optional<PipTouchHandler> pipTouchHandlerOptional, Transitions transitions) { - return new SplitscreenPipMixedHandler(shellInit, splitScreenOptional, - pipTouchHandlerOptional, transitions); + return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional, + pipTouchHandlerOptional); } // @@ -619,7 +619,7 @@ public abstract class WMShellModule { @ShellCreateTriggerOverride @Provides static Object provideIndependentShellComponentsToCreate( - SplitscreenPipMixedHandler splitscreenPipMixedHandler, + DefaultMixedHandler defaultMixedHandler, Optional<DesktopModeController> desktopModeController) { return new Object(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java index e26c259b2397..bcf4fbda0e0c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -35,10 +35,14 @@ import android.window.WindowContainerTransaction; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.pip.PipTransitionController; +import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.protolog.ShellProtoLogGroup; +import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.splitscreen.StageCoordinator; +import com.android.wm.shell.sysui.ShellInit; import java.util.ArrayList; +import java.util.Optional; /** * A handler for dealing with transitions involving multiple other handlers. For example: an @@ -47,8 +51,8 @@ import java.util.ArrayList; public class DefaultMixedHandler implements Transitions.TransitionHandler { private final Transitions mPlayer; - private final PipTransitionController mPipHandler; - private final StageCoordinator mSplitHandler; + private PipTransitionController mPipHandler; + private StageCoordinator mSplitHandler; private static class MixedTransition { static final int TYPE_ENTER_PIP_FROM_SPLIT = 1; @@ -77,13 +81,22 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler { mTransition = transition; } } + private final ArrayList<MixedTransition> mActiveTransitions = new ArrayList<>(); - public DefaultMixedHandler(@NonNull Transitions player, - @NonNull PipTransitionController pipHandler, @NonNull StageCoordinator splitHandler) { + public DefaultMixedHandler(@NonNull ShellInit shellInit, @NonNull Transitions player, + Optional<SplitScreenController> splitScreenControllerOptional, + Optional<PipTouchHandler> pipTouchHandlerOptional) { mPlayer = player; - mPipHandler = pipHandler; - mSplitHandler = splitHandler; + if (Transitions.ENABLE_SHELL_TRANSITIONS && pipTouchHandlerOptional.isPresent() + && splitScreenControllerOptional.isPresent()) { + // Add after dependencies because it is higher priority + shellInit.addInitCallback(() -> { + mPipHandler = pipTouchHandlerOptional.get().getTransitionHandler(); + mSplitHandler = splitScreenControllerOptional.get().getTransitionHandler(); + mPlayer.addHandler(this); + }, this); + } } @Nullable diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SplitscreenPipMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SplitscreenPipMixedHandler.java deleted file mode 100644 index 678e91fd8829..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SplitscreenPipMixedHandler.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2022 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.wm.shell.transition; - -import com.android.wm.shell.pip.phone.PipTouchHandler; -import com.android.wm.shell.splitscreen.SplitScreenController; -import com.android.wm.shell.sysui.ShellInit; - -import java.util.Optional; - -/** - * Handles transitions between the Splitscreen and PIP components. - */ -public class SplitscreenPipMixedHandler { - - private final Optional<SplitScreenController> mSplitScreenOptional; - private final Optional<PipTouchHandler> mPipTouchHandlerOptional; - private final Transitions mTransitions; - - public SplitscreenPipMixedHandler(ShellInit shellInit, - Optional<SplitScreenController> splitScreenControllerOptional, - Optional<PipTouchHandler> pipTouchHandlerOptional, - Transitions transitions) { - mSplitScreenOptional = splitScreenControllerOptional; - mPipTouchHandlerOptional = pipTouchHandlerOptional; - mTransitions = transitions; - if (Transitions.ENABLE_SHELL_TRANSITIONS - && mSplitScreenOptional.isPresent() && mPipTouchHandlerOptional.isPresent()) { - shellInit.addInitCallback(this::onInit, this); - } - } - - private void onInit() { - // Special handling for initializing based on multiple components - final DefaultMixedHandler mixedHandler = new DefaultMixedHandler(mTransitions, - mPipTouchHandlerOptional.get().getTransitionHandler(), - mSplitScreenOptional.get().getTransitionHandler()); - // Added at end so that it has highest priority. - mTransitions.addHandler(mixedHandler); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java index eab75b983268..c045cebdf4e0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java @@ -114,7 +114,8 @@ public class GroupedRecentTaskInfo implements Parcelable { /** * Get all {@link ActivityManager.RecentTaskInfo}s grouped together. */ - public List<ActivityManager.RecentTaskInfo> getAllTaskInfos() { + @NonNull + public List<ActivityManager.RecentTaskInfo> getTaskInfoList() { return Arrays.asList(mTasks); } @@ -148,7 +149,7 @@ public class GroupedRecentTaskInfo implements Parcelable { if (mSplitBounds != null) { taskString.append(", SplitBounds: ").append(mSplitBounds); } - taskString.append(", Type=").append(mType); + taskString.append(", Type="); switch (mType) { case TYPE_SINGLE: taskString.append("TYPE_SINGLE"); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt new file mode 100644 index 000000000000..baa06f2f0c45 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2022 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.wm.shell.recents + +import android.app.ActivityManager +import android.graphics.Rect +import android.os.Parcel +import android.testing.AndroidTestingRunner +import android.window.IWindowContainerToken +import android.window.WindowContainerToken +import androidx.test.filters.SmallTest +import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.util.GroupedRecentTaskInfo +import com.android.wm.shell.util.GroupedRecentTaskInfo.CREATOR +import com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM +import com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_SINGLE +import com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_SPLIT +import com.android.wm.shell.util.SplitBounds +import com.google.common.truth.Correspondence +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.mock + +/** + * Tests for [GroupedRecentTaskInfo] + */ +@SmallTest +@RunWith(AndroidTestingRunner::class) +class GroupedRecentTaskInfoTest : ShellTestCase() { + + @Test + fun testSingleTask_hasCorrectType() { + assertThat(singleTaskGroupInfo().type).isEqualTo(TYPE_SINGLE) + } + + @Test + fun testSingleTask_task1Set_task2Null() { + val group = singleTaskGroupInfo() + assertThat(group.taskInfo1.taskId).isEqualTo(1) + assertThat(group.taskInfo2).isNull() + } + + @Test + fun testSingleTask_taskInfoList_hasOneTask() { + val list = singleTaskGroupInfo().taskInfoList + assertThat(list).hasSize(1) + assertThat(list[0].taskId).isEqualTo(1) + } + + @Test + fun testSplitTasks_hasCorrectType() { + assertThat(splitTasksGroupInfo().type).isEqualTo(TYPE_SPLIT) + } + + @Test + fun testSplitTasks_task1Set_task2Set_boundsSet() { + val group = splitTasksGroupInfo() + assertThat(group.taskInfo1.taskId).isEqualTo(1) + assertThat(group.taskInfo2?.taskId).isEqualTo(2) + assertThat(group.splitBounds).isNotNull() + } + + @Test + fun testSplitTasks_taskInfoList_hasTwoTasks() { + val list = splitTasksGroupInfo().taskInfoList + assertThat(list).hasSize(2) + assertThat(list[0].taskId).isEqualTo(1) + assertThat(list[1].taskId).isEqualTo(2) + } + + @Test + fun testFreeformTasks_hasCorrectType() { + assertThat(freeformTasksGroupInfo().type).isEqualTo(TYPE_FREEFORM) + } + + @Test + fun testSplitTasks_taskInfoList_hasThreeTasks() { + val list = freeformTasksGroupInfo().taskInfoList + assertThat(list).hasSize(3) + assertThat(list[0].taskId).isEqualTo(1) + assertThat(list[1].taskId).isEqualTo(2) + assertThat(list[2].taskId).isEqualTo(3) + } + + @Test + fun testParcelling_singleTask() { + val recentTaskInfo = singleTaskGroupInfo() + val parcel = Parcel.obtain() + recentTaskInfo.writeToParcel(parcel, 0) + parcel.setDataPosition(0) + // Read the object back from the parcel + val recentTaskInfoParcel = CREATOR.createFromParcel(parcel) + assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_SINGLE) + assertThat(recentTaskInfoParcel.taskInfo1.taskId).isEqualTo(1) + assertThat(recentTaskInfoParcel.taskInfo2).isNull() + } + + @Test + fun testParcelling_splitTasks() { + val recentTaskInfo = splitTasksGroupInfo() + val parcel = Parcel.obtain() + recentTaskInfo.writeToParcel(parcel, 0) + parcel.setDataPosition(0) + // Read the object back from the parcel + val recentTaskInfoParcel = CREATOR.createFromParcel(parcel) + assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_SPLIT) + assertThat(recentTaskInfoParcel.taskInfo1.taskId).isEqualTo(1) + assertThat(recentTaskInfoParcel.taskInfo2).isNotNull() + assertThat(recentTaskInfoParcel.taskInfo2!!.taskId).isEqualTo(2) + assertThat(recentTaskInfoParcel.splitBounds).isNotNull() + } + + @Test + fun testParcelling_freeformTasks() { + val recentTaskInfo = freeformTasksGroupInfo() + val parcel = Parcel.obtain() + recentTaskInfo.writeToParcel(parcel, 0) + parcel.setDataPosition(0) + // Read the object back from the parcel + val recentTaskInfoParcel = CREATOR.createFromParcel(parcel) + assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_FREEFORM) + assertThat(recentTaskInfoParcel.taskInfoList).hasSize(3) + // Only compare task ids + val taskIdComparator = Correspondence.transforming<ActivityManager.RecentTaskInfo, Int>( + { it?.taskId }, "has taskId of" + ) + assertThat(recentTaskInfoParcel.taskInfoList).comparingElementsUsing(taskIdComparator) + .containsExactly(1, 2, 3) + } + + private fun createTaskInfo(id: Int) = ActivityManager.RecentTaskInfo().apply { + taskId = id + token = WindowContainerToken(mock(IWindowContainerToken::class.java)) + } + + private fun singleTaskGroupInfo(): GroupedRecentTaskInfo { + val task = createTaskInfo(id = 1) + return GroupedRecentTaskInfo.forSingleTask(task) + } + + private fun splitTasksGroupInfo(): GroupedRecentTaskInfo { + val task1 = createTaskInfo(id = 1) + val task2 = createTaskInfo(id = 2) + val splitBounds = SplitBounds(Rect(), Rect(), 1, 2) + return GroupedRecentTaskInfo.forSplitTasks(task1, task2, splitBounds) + } + + private fun freeformTasksGroupInfo(): GroupedRecentTaskInfo { + val task1 = createTaskInfo(id = 1) + val task2 = createTaskInfo(id = 2) + val task3 = createTaskInfo(id = 3) + return GroupedRecentTaskInfo.forFreeformTasks(task1, task2, task3) + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java index 9e755dca7908..e9a1e2523a86 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java @@ -213,8 +213,8 @@ public class RecentTasksControllerTest extends ShellTestCase { assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, singleGroup2.getType()); // Check freeform group entries - assertEquals(t1, freeformGroup.getAllTaskInfos().get(0)); - assertEquals(t3, freeformGroup.getAllTaskInfos().get(1)); + assertEquals(t1, freeformGroup.getTaskInfoList().get(0)); + assertEquals(t3, freeformGroup.getTaskInfoList().get(1)); // Check single entries assertEquals(t2, singleGroup1.getTaskInfo1()); diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 473afbd2aa2f..397975dcb78f 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -18,7 +18,6 @@ #include "CanvasProperty.h" #include "NinePatchUtils.h" -#include "SkBlendMode.h" #include "VectorDrawable.h" #include "hwui/Bitmap.h" #include "hwui/MinikinUtils.h" @@ -252,11 +251,10 @@ const SkiaCanvas::SaveRec* SkiaCanvas::currentSaveRec() const { return (rec && rec->saveCount == currentSaveCount) ? rec : nullptr; } -void SkiaCanvas::punchHole(const SkRRect& rect, float alpha) { +void SkiaCanvas::punchHole(const SkRRect& rect) { SkPaint paint = SkPaint(); - paint.setColor(SkColors::kBlack); - paint.setAlphaf(alpha); - paint.setBlendMode(SkBlendMode::kDstOut); + paint.setColor(0); + paint.setBlendMode(SkBlendMode::kClear); mCanvas->drawRRect(rect, paint); } diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index 51007c52260d..c6313f6c3a88 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -65,7 +65,7 @@ public: LOG_ALWAYS_FATAL("SkiaCanvas does not support enableZ"); } - virtual void punchHole(const SkRRect& rect, float alpha) override; + virtual void punchHole(const SkRRect& rect) override; virtual void setBitmap(const SkBitmap& bitmap) override; diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index 82d23b51b12a..7378351ef771 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -152,7 +152,7 @@ public: LOG_ALWAYS_FATAL("Not supported"); } - virtual void punchHole(const SkRRect& rect, float alpha) = 0; + virtual void punchHole(const SkRRect& rect) = 0; // ---------------------------------------------------------------------------- // Canvas state operations diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp index 0513447ed05e..fb7d5f72744d 100644 --- a/libs/hwui/jni/android_graphics_Canvas.cpp +++ b/libs/hwui/jni/android_graphics_Canvas.cpp @@ -713,10 +713,9 @@ static void setCompatibilityVersion(JNIEnv* env, jobject, jint apiLevel) { } static void punchHole(JNIEnv* env, jobject, jlong canvasPtr, jfloat left, jfloat top, jfloat right, - jfloat bottom, jfloat rx, jfloat ry, jfloat alpha) { + jfloat bottom, jfloat rx, jfloat ry) { auto canvas = reinterpret_cast<Canvas*>(canvasPtr); - canvas->punchHole(SkRRect::MakeRectXY(SkRect::MakeLTRB(left, top, right, bottom), rx, ry), - alpha); + canvas->punchHole(SkRRect::MakeRectXY(SkRect::MakeLTRB(left, top, right, bottom), rx, ry)); } }; // namespace CanvasJNI @@ -791,7 +790,7 @@ static const JNINativeMethod gDrawMethods[] = { {"nDrawTextRun","(JLjava/lang/String;IIIIFFZJ)V", (void*) CanvasJNI::drawTextRunString}, {"nDrawTextOnPath","(J[CIIJFFIJ)V", (void*) CanvasJNI::drawTextOnPathChars}, {"nDrawTextOnPath","(JLjava/lang/String;JFFIJ)V", (void*) CanvasJNI::drawTextOnPathString}, - {"nPunchHole", "(JFFFFFFF)V", (void*) CanvasJNI::punchHole} + {"nPunchHole", "(JFFFFFF)V", (void*) CanvasJNI::punchHole} }; int register_android_graphics_Canvas(JNIEnv* env) { diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp index f2282e661535..3bf2b2e63a47 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp @@ -201,7 +201,6 @@ protected: paint.setAlpha((uint8_t)paint.getAlpha() * mAlpha); return true; } - void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override { // We unroll the drawable using "this" canvas, so that draw calls contained inside will // get their alpha applied. The default SkPaintFilterCanvas::onDrawDrawable does not unroll. @@ -293,7 +292,7 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { // with the same canvas transformation + clip into the target // canvas then draw the layer on top if (renderNode->hasHolePunches()) { - TransformCanvas transformCanvas(canvas, SkBlendMode::kDstOut); + TransformCanvas transformCanvas(canvas, SkBlendMode::kClear); displayList->draw(&transformCanvas); } canvas->drawImageRect(snapshotImage, SkRect::Make(srcBounds), diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index 1f87865f2672..5c6117d86415 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -69,22 +69,20 @@ void SkiaRecordingCanvas::initDisplayList(uirenderer::RenderNode* renderNode, in mDisplayList->setHasHolePunches(false); } -void SkiaRecordingCanvas::punchHole(const SkRRect& rect, float alpha) { - // Add the marker annotation to allow HWUI to determine the current - // clip/transformation and alpha should be applied +void SkiaRecordingCanvas::punchHole(const SkRRect& rect) { + // Add the marker annotation to allow HWUI to determine where the current + // clip/transformation should be applied SkVector vector = rect.getSimpleRadii(); - float data[3]; + float data[2]; data[0] = vector.x(); data[1] = vector.y(); - data[2] = alpha; mRecorder.drawAnnotation(rect.rect(), HOLE_PUNCH_ANNOTATION.c_str(), - SkData::MakeWithCopy(data, sizeof(data))); + SkData::MakeWithCopy(data, 2 * sizeof(float))); // Clear the current rect within the layer itself SkPaint paint = SkPaint(); - paint.setColor(SkColors::kBlack); - paint.setAlphaf(alpha); - paint.setBlendMode(SkBlendMode::kDstOut); + paint.setColor(0); + paint.setBlendMode(SkBlendMode::kClear); mRecorder.drawRRect(rect, paint); mDisplayList->setHasHolePunches(true); diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h index 7844e2cc2a73..89e3a2c24e1e 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h @@ -50,7 +50,7 @@ public: initDisplayList(renderNode, width, height); } - virtual void punchHole(const SkRRect& rect, float alpha) override; + virtual void punchHole(const SkRRect& rect) override; virtual void finishRecording(uirenderer::RenderNode* destination) override; std::unique_ptr<SkiaDisplayList> finishRecording(); diff --git a/libs/hwui/pipeline/skia/TransformCanvas.cpp b/libs/hwui/pipeline/skia/TransformCanvas.cpp index c320df035d08..33160d05e2d0 100644 --- a/libs/hwui/pipeline/skia/TransformCanvas.cpp +++ b/libs/hwui/pipeline/skia/TransformCanvas.cpp @@ -29,15 +29,13 @@ using namespace android::uirenderer::skiapipeline; void TransformCanvas::onDrawAnnotation(const SkRect& rect, const char* key, SkData* value) { if (HOLE_PUNCH_ANNOTATION == key) { auto* rectParams = reinterpret_cast<const float*>(value->data()); - const float radiusX = rectParams[0]; - const float radiusY = rectParams[1]; - const float alpha = rectParams[2]; + float radiusX = rectParams[0]; + float radiusY = rectParams[1]; SkRRect roundRect = SkRRect::MakeRectXY(rect, radiusX, radiusY); SkPaint paint; paint.setColor(SkColors::kBlack); paint.setBlendMode(mHolePunchBlendMode); - paint.setAlphaf(alpha); mWrappedCanvas->drawRRect(roundRect, paint); } } diff --git a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp index 7d3ca9642458..59230a754f4e 100644 --- a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp +++ b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp @@ -138,7 +138,7 @@ private: roundRectPaint.setColor(Color::White); if (addHolePunch) { // Punch a hole but then cover it up, we don't want to actually see it - canvas.punchHole(SkRRect::MakeRect(SkRect::MakeWH(itemWidth, itemHeight)), 1.f); + canvas.punchHole(SkRRect::MakeRect(SkRect::MakeWH(itemWidth, itemHeight))); } canvas.drawRoundRect(0, 0, itemWidth, itemHeight, dp(6), dp(6), roundRectPaint); @@ -235,4 +235,4 @@ class StretchyUniformLayerListViewHolePunch : public StretchyListViewAnimation { StretchEffectBehavior stretchBehavior() override { return StretchEffectBehavior::UniformScale; } bool haveHolePunch() override { return true; } bool forceLayer() override { return true; } -}; +};
\ No newline at end of file diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt new file mode 100644 index 000000000000..b00661575c14 --- /dev/null +++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2022 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.internal.systemui.lint + +import com.android.tools.lint.detector.api.Category +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Implementation +import com.android.tools.lint.detector.api.Issue +import com.android.tools.lint.detector.api.JavaContext +import com.android.tools.lint.detector.api.Scope +import com.android.tools.lint.detector.api.Severity +import com.android.tools.lint.detector.api.SourceCodeScanner +import com.intellij.psi.PsiMethod +import org.jetbrains.uast.UCallExpression + +/** + * Checks for slow calls to ActivityManager.getCurrentUser() or UserManager.getUserInfo() and + * suggests using UserTracker instead. For more info, see: http://go/multi-user-in-systemui-slides. + */ +@Suppress("UnstableApiUsage") +class SlowUserQueryDetector : Detector(), SourceCodeScanner { + + override fun getApplicableMethodNames(): List<String> { + return listOf("getCurrentUser", "getUserInfo") + } + + override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) { + val evaluator = context.evaluator + if ( + evaluator.isStatic(method) && + method.name == "getCurrentUser" && + method.containingClass?.qualifiedName == "android.app.ActivityManager" + ) { + context.report( + ISSUE_SLOW_USER_ID_QUERY, + method, + context.getNameLocation(node), + "ActivityManager.getCurrentUser() is slow. " + + "Use UserTracker.getUserId() instead." + ) + } + if ( + !evaluator.isStatic(method) && + method.name == "getUserInfo" && + method.containingClass?.qualifiedName == "android.os.UserManager" + ) { + context.report( + ISSUE_SLOW_USER_INFO_QUERY, + method, + context.getNameLocation(node), + "UserManager.getUserInfo() is slow. " + "Use UserTracker.getUserInfo() instead." + ) + } + } + + companion object { + @JvmField + val ISSUE_SLOW_USER_ID_QUERY: Issue = + Issue.create( + id = "SlowUserIdQuery", + briefDescription = "User ID queried using ActivityManager instead of UserTracker.", + explanation = + "ActivityManager.getCurrentUser() makes a binder call and is slow. " + + "Instead, inject a UserTracker and call UserTracker.getUserId(). For " + + "more info, see: http://go/multi-user-in-systemui-slides", + category = Category.PERFORMANCE, + priority = 8, + severity = Severity.WARNING, + implementation = + Implementation(SlowUserQueryDetector::class.java, Scope.JAVA_FILE_SCOPE) + ) + + @JvmField + val ISSUE_SLOW_USER_INFO_QUERY: Issue = + Issue.create( + id = "SlowUserInfoQuery", + briefDescription = "User info queried using UserManager instead of UserTracker.", + explanation = + "UserManager.getUserInfo() makes a binder call and is slow. " + + "Instead, inject a UserTracker and call UserTracker.getUserInfo(). For " + + "more info, see: http://go/multi-user-in-systemui-slides", + category = Category.PERFORMANCE, + priority = 8, + severity = Severity.WARNING, + implementation = + Implementation(SlowUserQueryDetector::class.java, Scope.JAVA_FILE_SCOPE) + ) + } +} diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt index c7c73d3c86a1..4879883e7c2e 100644 --- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt +++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt @@ -30,6 +30,8 @@ class SystemUIIssueRegistry : IssueRegistry() { get() = listOf( BindServiceViaContextDetector.ISSUE, BroadcastSentViaContextDetector.ISSUE, + SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY, + SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY, GetMainLooperViaContextDetector.ISSUE, RegisterReceiverViaContextDetector.ISSUE, SoftwareBitmapDetector.ISSUE, diff --git a/packages/SystemUI/checks/tests/com/android/systemui/lint/SlowUserQueryDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/systemui/lint/SlowUserQueryDetectorTest.kt new file mode 100644 index 000000000000..2738f0409fd0 --- /dev/null +++ b/packages/SystemUI/checks/tests/com/android/systemui/lint/SlowUserQueryDetectorTest.kt @@ -0,0 +1,194 @@ +package com.android.internal.systemui.lint + +import com.android.tools.lint.checks.infrastructure.LintDetectorTest +import com.android.tools.lint.checks.infrastructure.TestFile +import com.android.tools.lint.checks.infrastructure.TestFiles +import com.android.tools.lint.checks.infrastructure.TestLintTask +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Issue +import org.junit.Test + +class SlowUserQueryDetectorTest : LintDetectorTest() { + + override fun getDetector(): Detector = SlowUserQueryDetector() + override fun lint(): TestLintTask = super.lint().allowMissingSdk(true) + + override fun getIssues(): List<Issue> = + listOf( + SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY, + SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY + ) + + @Test + fun testGetCurrentUser() { + lint() + .files( + TestFiles.java( + """ + package test.pkg; + import android.app.ActivityManager; + + public class TestClass1 { + public void slewlyGetCurrentUser() { + ActivityManager.getCurrentUser(); + } + } + """ + ) + .indented(), + *stubs + ) + .issues( + SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY, + SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY + ) + .run() + .expectWarningCount(1) + .expectContains( + "ActivityManager.getCurrentUser() is slow. " + + "Use UserTracker.getUserId() instead." + ) + } + + @Test + fun testGetUserInfo() { + lint() + .files( + TestFiles.java( + """ + package test.pkg; + import android.os.UserManager; + + public class TestClass2 { + public void slewlyGetUserInfo(UserManager userManager) { + userManager.getUserInfo(); + } + } + """ + ) + .indented(), + *stubs + ) + .issues( + SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY, + SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY + ) + .run() + .expectWarningCount(1) + .expectContains( + "UserManager.getUserInfo() is slow. " + "Use UserTracker.getUserInfo() instead." + ) + } + + @Test + fun testUserTrackerGetUserId() { + lint() + .files( + TestFiles.java( + """ + package test.pkg; + import com.android.systemui.settings.UserTracker; + + public class TestClass3 { + public void quicklyGetUserId(UserTracker userTracker) { + userTracker.getUserId(); + } + } + """ + ) + .indented(), + *stubs + ) + .issues( + SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY, + SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY + ) + .run() + .expectClean() + } + + @Test + fun testUserTrackerGetUserInfo() { + lint() + .files( + TestFiles.java( + """ + package test.pkg; + import com.android.systemui.settings.UserTracker; + + public class TestClass4 { + public void quicklyGetUserId(UserTracker userTracker) { + userTracker.getUserInfo(); + } + } + """ + ) + .indented(), + *stubs + ) + .issues( + SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY, + SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY + ) + .run() + .expectClean() + } + + private val activityManagerStub: TestFile = + java( + """ + package android.app; + + public class ActivityManager { + public static int getCurrentUser() {}; + } + """ + ) + + private val userManagerStub: TestFile = + java( + """ + package android.os; + import android.content.pm.UserInfo; + import android.annotation.UserIdInt; + + public class UserManager { + public UserInfo getUserInfo(@UserIdInt int userId) {}; + } + """ + ) + + private val userIdIntStub: TestFile = + java( + """ + package android.annotation; + + public @interface UserIdInt {} + """ + ) + + private val userInfoStub: TestFile = + java( + """ + package android.content.pm; + + public class UserInfo {} + """ + ) + + private val userTrackerStub: TestFile = + java( + """ + package com.android.systemui.settings; + import android.content.pm.UserInfo; + + public interface UserTracker { + public int getUserId(); + public UserInfo getUserInfo(); + } + """ + ) + + private val stubs = + arrayOf(activityManagerStub, userManagerStub, userIdIntStub, userInfoStub, userTrackerStub) +} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt new file mode 100644 index 000000000000..02d76f404a32 --- /dev/null +++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2022 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.user + +import android.content.Context +import androidx.appcompat.content.res.AppCompatResources +import com.android.systemui.common.shared.model.Text +import com.android.systemui.compose.gallery.R +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.power.data.repository.FakePowerRepository +import com.android.systemui.power.domain.interactor.PowerInteractor +import com.android.systemui.user.data.repository.FakeUserRepository +import com.android.systemui.user.domain.interactor.UserInteractor +import com.android.systemui.user.shared.model.UserActionModel +import com.android.systemui.user.shared.model.UserModel +import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel +import com.android.systemui.util.mockito.mock + +object Fakes { + private val USER_TINT_COLORS = + arrayOf( + 0x000000, + 0x0000ff, + 0x00ff00, + 0x00ffff, + 0xff0000, + 0xff00ff, + 0xffff00, + 0xffffff, + ) + + fun fakeUserSwitcherViewModel( + context: Context, + userCount: Int, + ): UserSwitcherViewModel { + return UserSwitcherViewModel.Factory( + userInteractor = + UserInteractor( + repository = + FakeUserRepository().apply { + setUsers( + (0 until userCount).map { index -> + UserModel( + id = index, + name = Text.Loaded("user_$index"), + image = + checkNotNull( + AppCompatResources.getDrawable( + context, + R.drawable.ic_avatar_guest_user + ) + ), + isSelected = index == 0, + isSelectable = true, + ) + } + ) + setActions( + UserActionModel.values().mapNotNull { + if (it == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT) { + null + } else { + it + } + } + ) + }, + controller = mock(), + activityStarter = mock(), + keyguardInteractor = + KeyguardInteractor( + repository = + FakeKeyguardRepository().apply { setKeyguardShowing(false) }, + ), + ), + powerInteractor = + PowerInteractor( + repository = FakePowerRepository(), + ) + ) + .create(UserSwitcherViewModel::class.java) + } +} diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt index 72757121d53f..bfbccb85b13f 100644 --- a/packages/SystemUI/ktfmt_includes.txt +++ b/packages/SystemUI/ktfmt_includes.txt @@ -240,8 +240,6 @@ -packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesManager.kt -packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt -packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt --packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt --packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt -packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt -packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt -packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt @@ -529,6 +527,8 @@ -packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt -packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowModule.kt -packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt +-packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt +-packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt -packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt -packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt -packages/SystemUI/src/com/android/systemui/tv/TVSystemUICoreStartableModule.kt @@ -677,7 +677,6 @@ -packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt -packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesManagerTest.kt -packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt --packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt -packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt -packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt -packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt @@ -834,6 +833,7 @@ -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/WalletControllerImplTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt +-packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt -packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt -packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt diff --git a/packages/SystemUI/res/drawable/media_seekbar_thumb.xml b/packages/SystemUI/res/drawable/media_seekbar_thumb.xml new file mode 100644 index 000000000000..5eb2bfdbee39 --- /dev/null +++ b/packages/SystemUI/res/drawable/media_seekbar_thumb.xml @@ -0,0 +1,50 @@ +<!-- + ~ Copyright (C) 2022 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. + --> + +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <aapt:attr name="android:drawable"> + <vector android:height="16dp" + android:width="4dp" + android:viewportHeight="16" + android:viewportWidth="4"> + <group android:name="_R_G"> + <group android:name="_R_G_L_0_G" + android:translateX="2" + android:translateY="8"> + <path android:name="_R_G_L_0_G_D_0_P_0" + android:fillColor="#ffffff" + android:fillAlpha="1" + android:fillType="nonZero" + android:pathData=" M2 -6 C2,-6 2,6 2,6 C2,7.1 1.1,8 0,8 C0,8 0,8 0,8 C-1.1,8 -2,7.1 -2,6 C-2,6 -2,-6 -2,-6 C-2,-7.1 -1.1,-8 0,-8 C0,-8 0,-8 0,-8 C1.1,-8 2,-7.1 2,-6c "/> + </group> + </group> + <group android:name="time_group"/> + </vector> + </aapt:attr> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="translateX" + android:duration="1017" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType"/> + </set> + </aapt:attr> + </target> +</animated-vector> diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml index 0ca19d98a097..8df8c49ee057 100644 --- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml +++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml @@ -83,48 +83,6 @@ android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset" android:visibility="gone" /> - <ImageView - android:id="@+id/wallet_button" - android:layout_height="@dimen/keyguard_affordance_fixed_height" - android:layout_width="@dimen/keyguard_affordance_fixed_width" - android:layout_gravity="bottom|end" - android:scaleType="center" - android:tint="?android:attr/textColorPrimary" - android:src="@drawable/ic_wallet_lockscreen" - android:background="@drawable/keyguard_bottom_affordance_bg" - android:layout_marginEnd="@dimen/keyguard_affordance_horizontal_offset" - android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset" - android:contentDescription="@string/accessibility_wallet_button" - android:visibility="gone" /> - - <ImageView - android:id="@+id/qr_code_scanner_button" - android:layout_height="@dimen/keyguard_affordance_fixed_height" - android:layout_width="@dimen/keyguard_affordance_fixed_width" - android:layout_gravity="bottom|end" - android:scaleType="center" - android:tint="?android:attr/textColorPrimary" - android:src="@drawable/ic_qr_code_scanner" - android:background="@drawable/keyguard_bottom_affordance_bg" - android:layout_marginEnd="@dimen/keyguard_affordance_horizontal_offset" - android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset" - android:contentDescription="@string/accessibility_qr_code_scanner_button" - android:visibility="gone" /> - - <ImageView - android:id="@+id/controls_button" - android:layout_height="@dimen/keyguard_affordance_fixed_height" - android:layout_width="@dimen/keyguard_affordance_fixed_width" - android:layout_gravity="bottom|start" - android:scaleType="center" - android:tint="?android:attr/textColorPrimary" - android:src="@drawable/controls_icon" - android:background="@drawable/keyguard_bottom_affordance_bg" - android:layout_marginStart="@dimen/keyguard_affordance_horizontal_offset" - android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset" - android:contentDescription="@string/quick_controls_title" - android:visibility="gone" /> - <FrameLayout android:id="@+id/overlay_container" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 8af432a03ab3..a734fa744b48 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -580,6 +580,7 @@ </style> <style name="MediaPlayer.ProgressBar" parent="@android:style/Widget.ProgressBar.Horizontal"> + <item name="android:thumb">@drawable/media_seekbar_thumb</item> <item name="android:thumbTint">?android:attr/textColorPrimary</item> <item name="android:progressDrawable">@drawable/media_squiggly_progress</item> <item name="android:progressTint">?android:attr/textColorPrimary</item> diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp index f59a3200976a..9040ea176e4f 100644 --- a/packages/SystemUI/shared/Android.bp +++ b/packages/SystemUI/shared/Android.bp @@ -52,7 +52,7 @@ android_library { "SystemUIUnfoldLib", "androidx.dynamicanimation_dynamicanimation", "androidx.concurrent_concurrent-futures", - "gson-prebuilt-jar", + "gson", "dagger2", "jsr330", ], diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt index acbea1beeae3..7d6f377d5287 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt @@ -50,12 +50,10 @@ constructor( viewsIdToTranslate = setOf( ViewIdToTranslate(R.id.keyguard_status_area, LEFT, filterNever), - ViewIdToTranslate(R.id.controls_button, LEFT, filterNever), ViewIdToTranslate(R.id.lockscreen_clock_view_large, LEFT, filterSplitShadeOnly), ViewIdToTranslate(R.id.lockscreen_clock_view, LEFT, filterNever), ViewIdToTranslate( R.id.notification_stack_scroller, RIGHT, filterSplitShadeOnly), - ViewIdToTranslate(R.id.wallet_button, RIGHT, filterNever), ViewIdToTranslate(R.id.start_button, LEFT, filterNever), ViewIdToTranslate(R.id.end_button, RIGHT, filterNever)), progressProvider = unfoldProgressProvider) diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 094c4a772d05..1068c261b68e 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -451,6 +451,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab FACE_AUTH_TRIGGERED_TRUST_DISABLED); } + mLogger.logTrustChanged(wasTrusted, enabled, userId); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { @@ -466,12 +467,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab final boolean userHasTrust = getUserHasTrust(userId); if (userHasTrust && trustGrantedMessages != null) { for (String msg : trustGrantedMessages) { - if (!TextUtils.isEmpty(msg)) { - message = msg; + message = msg; + if (!TextUtils.isEmpty(message)) { break; } } } + + if (message != null) { + mLogger.logShowTrustGrantedMessage(message.toString()); + } for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { @@ -728,6 +733,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mFingerprintCancelSignal = null; updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, FACE_AUTH_UPDATED_FP_AUTHENTICATED); + mLogger.d("onFingerprintAuthenticated"); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { @@ -971,6 +977,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mFaceCancelSignal = null; updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, FACE_AUTH_UPDATED_ON_FACE_AUTHENTICATED); + mLogger.d("onFaceAuthenticated"); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { @@ -3431,6 +3438,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mUserFaceAuthenticated.clear(); mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FINGERPRINT, unlockedUser); mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FACE, unlockedUser); + mLogger.d("clearBiometricRecognized"); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); @@ -3680,6 +3688,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @Override public void dump(PrintWriter pw, String[] args) { pw.println("KeyguardUpdateMonitor state:"); + pw.println(" getUserHasTrust()=" + getUserHasTrust(getCurrentUser())); + pw.println(" getUserUnlockedWithBiometric()=" + + getUserUnlockedWithBiometric(getCurrentUser())); pw.println(" SIM States:"); for (SimData data : mSimDatas.values()) { pw.println(" " + data.toString()); diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt index 2bc98f1a535c..7a00cd930f2a 100644 --- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt +++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt @@ -340,4 +340,40 @@ class KeyguardUpdateMonitorLogger @Inject constructor( bool1 = dismissKeyguard }, { "reportUserRequestedUnlock origin=$str1 reason=$str2 dismissKeyguard=$bool1" }) } + + fun logShowTrustGrantedMessage( + message: String + ) { + logBuffer.log(TAG, DEBUG, { + str1 = message + }, { "showTrustGrantedMessage message$str1" }) + } + + fun logTrustChanged( + wasTrusted: Boolean, + isNowTrusted: Boolean, + userId: Int + ) { + logBuffer.log(TAG, DEBUG, { + bool1 = wasTrusted + bool2 = isNowTrusted + int1 = userId + }, { "onTrustChanged[user=$int1] wasTrusted=$bool1 isNowTrusted=$bool2" }) + } + + fun logKeyguardStateUpdate( + secure: Boolean, + canDismissLockScreen: Boolean, + trusted: Boolean, + trustManaged: Boolean + + ) { + logBuffer.log("KeyguardState", DEBUG, { + bool1 = secure + bool2 = canDismissLockScreen + bool3 = trusted + bool4 = trustManaged + }, { "#update secure=$bool1 canDismissKeyguard=$bool2" + + " trusted=$bool3 trustManaged=$bool4" }) + } } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java index de7242c6b3d4..78099d1fd12f 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java @@ -92,12 +92,6 @@ public class Flags { public static final ResourceBooleanFlag FACE_SCANNING_ANIM = new ResourceBooleanFlag(205, R.bool.config_enableFaceScanningAnimation); - /** - * Whether the KeyguardBottomArea(View|Controller) should use the modern architecture or the old - * one. - */ - public static final ReleasedFlag MODERN_BOTTOM_AREA = new ReleasedFlag(206, true); - public static final UnreleasedFlag LOCKSCREEN_CUSTOM_CLOCKS = new UnreleasedFlag(207); /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index 898959e6eb58..ee5ef9905819 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -29,6 +29,7 @@ import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE; +import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_OLD_NONE; import static android.view.WindowManager.TRANSIT_OPEN; @@ -160,6 +161,9 @@ public class KeyguardService extends Service { return apps.length == 0 ? TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER : TRANSIT_OLD_KEYGUARD_GOING_AWAY; } else if (type == TRANSIT_KEYGUARD_OCCLUDE) { + boolean isOccludeByDream = apps.length > 0 && apps[0].taskInfo.topActivityType + == WindowConfiguration.ACTIVITY_TYPE_DREAM; + if (isOccludeByDream) return TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM; return TRANSIT_OLD_KEYGUARD_OCCLUDE; } else if (type == TRANSIT_KEYGUARD_UNOCCLUDE) { return TRANSIT_OLD_KEYGUARD_UNOCCLUDE; @@ -271,6 +275,12 @@ public class KeyguardService extends Service { definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_OCCLUDE, occludeAnimationAdapter); + final RemoteAnimationAdapter occludeByDreamAnimationAdapter = + new RemoteAnimationAdapter( + mKeyguardViewMediator.getOccludeByDreamAnimationRunner(), 0, 0); + definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM, + occludeByDreamAnimationAdapter); + final RemoteAnimationAdapter unoccludeAnimationAdapter = new RemoteAnimationAdapter( mKeyguardViewMediator.getUnoccludeAnimationRunner(), 0, 0); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 2c60d5d67dac..dc034360ad09 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -887,6 +887,86 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, private IRemoteAnimationRunner mOccludeAnimationRunner = new OccludeActivityLaunchRemoteAnimationRunner(mOccludeAnimationController); + private final IRemoteAnimationRunner mOccludeByDreamAnimationRunner = + new IRemoteAnimationRunner.Stub() { + @Nullable private ValueAnimator mOccludeByDreamAnimator; + + @Override + public void onAnimationCancelled(boolean isKeyguardOccluded) { + if (mOccludeByDreamAnimator != null) { + mOccludeByDreamAnimator.cancel(); + } + setOccluded(isKeyguardOccluded /* isOccluded */, false /* animate */); + if (DEBUG) { + Log.d(TAG, "Occlude by Dream animation cancelled. Occluded state is now: " + + mOccluded); + } + } + + @Override + public void onAnimationStart(int transit, RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, + IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException { + setOccluded(true /* isOccluded */, true /* animate */); + + if (apps == null || apps.length == 0 || apps[0] == null) { + if (DEBUG) { + Log.d(TAG, "No apps provided to the OccludeByDream runner; " + + "skipping occluding animation."); + } + finishedCallback.onAnimationFinished(); + return; + } + + final RemoteAnimationTarget primary = apps[0]; + final boolean isDream = (apps[0].taskInfo.topActivityType + == WindowConfiguration.ACTIVITY_TYPE_DREAM); + if (!isDream) { + Log.w(TAG, "The occluding app isn't Dream; " + + "finishing up. Please check that the config is correct."); + finishedCallback.onAnimationFinished(); + return; + } + + final SyncRtSurfaceTransactionApplier applier = + new SyncRtSurfaceTransactionApplier( + mKeyguardViewControllerLazy.get().getViewRootImpl().getView()); + + mContext.getMainExecutor().execute(() -> { + if (mOccludeByDreamAnimator != null) { + mOccludeByDreamAnimator.cancel(); + } + + mOccludeByDreamAnimator = ValueAnimator.ofFloat(0f, 1f); + // Use the same duration as for the UNOCCLUDE. + mOccludeByDreamAnimator.setDuration(UNOCCLUDE_ANIMATION_DURATION); + mOccludeByDreamAnimator.setInterpolator(Interpolators.LINEAR); + mOccludeByDreamAnimator.addUpdateListener( + animation -> { + SyncRtSurfaceTransactionApplier.SurfaceParams.Builder + paramsBuilder = + new SyncRtSurfaceTransactionApplier.SurfaceParams + .Builder(primary.leash) + .withAlpha(animation.getAnimatedFraction()); + applier.scheduleApply(paramsBuilder.build()); + }); + mOccludeByDreamAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + try { + finishedCallback.onAnimationFinished(); + mOccludeByDreamAnimator = null; + } catch (RemoteException e) { + e.printStackTrace(); + } + } + }); + + mOccludeByDreamAnimator.start(); + }); + } + }; + /** * Animation controller for activities that unocclude the keyguard. This does not use the * ActivityLaunchAnimator since we're just translating down, rather than emerging from a view @@ -1682,6 +1762,10 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, return mOccludeAnimationRunner; } + public IRemoteAnimationRunner getOccludeByDreamAnimationRunner() { + return mOccludeByDreamAnimationRunner; + } + public IRemoteAnimationRunner getUnoccludeAnimationRunner() { return mUnoccludeAnimationRunner; } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt index 8f32ff9db50c..ac2c9b1d7ff2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt @@ -94,6 +94,7 @@ constructor( hasFavorites = favorites?.isNotEmpty() == true, hasServiceInfos = serviceInfos.isNotEmpty(), iconResourceId = component.getTileImageId(), + visibility = component.getVisibility(), ), TAG, ) @@ -110,9 +111,16 @@ constructor( isFeatureEnabled: Boolean, hasFavorites: Boolean, hasServiceInfos: Boolean, + visibility: ControlsComponent.Visibility, @DrawableRes iconResourceId: Int?, ): KeyguardQuickAffordanceConfig.State { - return if (isFeatureEnabled && hasFavorites && hasServiceInfos && iconResourceId != null) { + return if ( + isFeatureEnabled && + hasFavorites && + hasServiceInfos && + iconResourceId != null && + visibility == ControlsComponent.Visibility.AVAILABLE + ) { KeyguardQuickAffordanceConfig.State.Visible( icon = ContainedDrawable.WithResource(iconResourceId), contentDescriptionResourceId = component.getTileTitleId(), diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt index 35a6c74518e0..5d6d683f93f6 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt @@ -34,15 +34,14 @@ import com.android.settingslib.Utils import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.media.taptotransfer.common.ChipInfoCommon -import com.android.systemui.media.taptotransfer.common.DEFAULT_TIMEOUT_MILLIS -import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon import com.android.systemui.media.taptotransfer.common.MediaTttLogger import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.temporarydisplay.DEFAULT_TIMEOUT_MILLIS +import com.android.systemui.temporarydisplay.TemporaryViewDisplayController +import com.android.systemui.temporarydisplay.TemporaryViewInfo import com.android.systemui.util.animation.AnimationUtil.Companion.frames import com.android.systemui.util.concurrency.DelayableExecutor -import com.android.systemui.util.view.ViewUtil import javax.inject.Inject /** @@ -56,18 +55,16 @@ class MediaTttChipControllerReceiver @Inject constructor( context: Context, @MediaTttReceiverLogger logger: MediaTttLogger, windowManager: WindowManager, - viewUtil: ViewUtil, mainExecutor: DelayableExecutor, accessibilityManager: AccessibilityManager, configurationController: ConfigurationController, powerManager: PowerManager, @Main private val mainHandler: Handler, private val uiEventLogger: MediaTttReceiverUiEventLogger, -) : MediaTttChipControllerCommon<ChipReceiverInfo>( +) : TemporaryViewDisplayController<ChipReceiverInfo>( context, logger, windowManager, - viewUtil, mainExecutor, accessibilityManager, configurationController, @@ -119,18 +116,18 @@ class MediaTttChipControllerReceiver @Inject constructor( uiEventLogger.logReceiverStateChange(chipState) if (chipState == ChipStateReceiver.FAR_FROM_SENDER) { - removeChip(removalReason = ChipStateReceiver.FAR_FROM_SENDER::class.simpleName!!) + removeView(removalReason = ChipStateReceiver.FAR_FROM_SENDER::class.simpleName!!) return } if (appIcon == null) { - displayChip(ChipReceiverInfo(routeInfo, appIconDrawableOverride = null, appName)) + displayView(ChipReceiverInfo(routeInfo, appIconDrawableOverride = null, appName)) return } appIcon.loadDrawableAsync( context, Icon.OnDrawableLoadedListener { drawable -> - displayChip(ChipReceiverInfo(routeInfo, drawable, appName)) + displayView(ChipReceiverInfo(routeInfo, drawable, appName)) }, // Notify the listener on the main handler since the listener will update // the UI. @@ -138,19 +135,19 @@ class MediaTttChipControllerReceiver @Inject constructor( ) } - override fun updateChipView(newChipInfo: ChipReceiverInfo, currentChipView: ViewGroup) { - super.updateChipView(newChipInfo, currentChipView) + override fun updateView(newInfo: ChipReceiverInfo, currentView: ViewGroup) { + super.updateView(newInfo, currentView) val iconName = setIcon( - currentChipView, - newChipInfo.routeInfo.clientPackageName, - newChipInfo.appIconDrawableOverride, - newChipInfo.appNameOverride + currentView, + newInfo.routeInfo.clientPackageName, + newInfo.appIconDrawableOverride, + newInfo.appNameOverride ) - currentChipView.contentDescription = iconName + currentView.contentDescription = iconName } - override fun animateChipIn(chipView: ViewGroup) { - val appIconView = chipView.requireViewById<View>(R.id.app_icon) + override fun animateViewIn(view: ViewGroup) { + val appIconView = view.requireViewById<View>(R.id.app_icon) appIconView.animate() .translationYBy(-1 * getTranslationAmount().toFloat()) .setDuration(30.frames) @@ -160,8 +157,8 @@ class MediaTttChipControllerReceiver @Inject constructor( .setDuration(5.frames) .start() // Using withEndAction{} doesn't apply a11y focus when screen is unlocked. - appIconView.postOnAnimation { chipView.requestAccessibilityFocus() } - startRipple(chipView.requireViewById(R.id.ripple)) + appIconView.postOnAnimation { view.requestAccessibilityFocus() } + startRipple(view.requireViewById(R.id.ripple)) } override fun getIconSize(isAppIcon: Boolean): Int? = @@ -216,7 +213,7 @@ data class ChipReceiverInfo( val routeInfo: MediaRoute2Info, val appIconDrawableOverride: Drawable?, val appNameOverride: CharSequence? -) : ChipInfoCommon { +) : TemporaryViewInfo { override fun getTimeoutMs() = DEFAULT_TIMEOUT_MILLIS } diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt index a153cb6c0d31..bde588c14fc8 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt @@ -25,7 +25,7 @@ import androidx.annotation.StringRes import com.android.internal.logging.UiEventLogger import com.android.internal.statusbar.IUndoMediaTransferCallback import com.android.systemui.R -import com.android.systemui.media.taptotransfer.common.DEFAULT_TIMEOUT_MILLIS +import com.android.systemui.temporarydisplay.DEFAULT_TIMEOUT_MILLIS /** * A class enumerating all the possible states of the media tap-to-transfer chip on the sender @@ -120,7 +120,7 @@ enum class ChipStateSender( // state, but that may take too long to go through the binder and the user may be // confused ast o why the UI hasn't changed yet. So, we immediately change the UI // here. - controllerSender.displayChip( + controllerSender.displayView( ChipSenderInfo( TRANSFER_TO_THIS_DEVICE_TRIGGERED, routeInfo, undoCallback ) @@ -155,7 +155,7 @@ enum class ChipStateSender( // state, but that may take too long to go through the binder and the user may be // confused as to why the UI hasn't changed yet. So, we immediately change the UI // here. - controllerSender.displayChip( + controllerSender.displayView( ChipSenderInfo( TRANSFER_TO_RECEIVER_TRIGGERED, routeInfo, undoCallback ) diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt index 933548963390..0c1ebd70c572 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt @@ -33,14 +33,13 @@ import com.android.systemui.animation.Interpolators import com.android.systemui.animation.ViewHierarchyAnimator import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.media.taptotransfer.common.ChipInfoCommon -import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon import com.android.systemui.media.taptotransfer.common.MediaTttLogger -import com.android.systemui.media.taptotransfer.common.MediaTttRemovalReason import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.temporarydisplay.TemporaryDisplayRemovalReason +import com.android.systemui.temporarydisplay.TemporaryViewDisplayController +import com.android.systemui.temporarydisplay.TemporaryViewInfo import com.android.systemui.util.concurrency.DelayableExecutor -import com.android.systemui.util.view.ViewUtil import javax.inject.Inject /** @@ -53,17 +52,15 @@ class MediaTttChipControllerSender @Inject constructor( context: Context, @MediaTttSenderLogger logger: MediaTttLogger, windowManager: WindowManager, - viewUtil: ViewUtil, @Main mainExecutor: DelayableExecutor, accessibilityManager: AccessibilityManager, configurationController: ConfigurationController, powerManager: PowerManager, private val uiEventLogger: MediaTttSenderUiEventLogger -) : MediaTttChipControllerCommon<ChipSenderInfo>( +) : TemporaryViewDisplayController<ChipSenderInfo>( context, logger, windowManager, - viewUtil, mainExecutor, accessibilityManager, configurationController, @@ -106,53 +103,52 @@ class MediaTttChipControllerSender @Inject constructor( uiEventLogger.logSenderStateChange(chipState) if (chipState == ChipStateSender.FAR_FROM_RECEIVER) { - removeChip(removalReason = ChipStateSender.FAR_FROM_RECEIVER::class.simpleName!!) + removeView(removalReason = ChipStateSender.FAR_FROM_RECEIVER::class.simpleName!!) } else { - displayChip(ChipSenderInfo(chipState, routeInfo, undoCallback)) + displayView(ChipSenderInfo(chipState, routeInfo, undoCallback)) } } - /** Displays the chip view for the given state. */ - override fun updateChipView( - newChipInfo: ChipSenderInfo, - currentChipView: ViewGroup + override fun updateView( + newInfo: ChipSenderInfo, + currentView: ViewGroup ) { - super.updateChipView(newChipInfo, currentChipView) + super.updateView(newInfo, currentView) - val chipState = newChipInfo.state + val chipState = newInfo.state // App icon - val iconName = setIcon(currentChipView, newChipInfo.routeInfo.clientPackageName) + val iconName = setIcon(currentView, newInfo.routeInfo.clientPackageName) // Text - val otherDeviceName = newChipInfo.routeInfo.name.toString() + val otherDeviceName = newInfo.routeInfo.name.toString() val chipText = chipState.getChipTextString(context, otherDeviceName) - currentChipView.requireViewById<TextView>(R.id.text).text = chipText + currentView.requireViewById<TextView>(R.id.text).text = chipText // Loading - currentChipView.requireViewById<View>(R.id.loading).visibility = + currentView.requireViewById<View>(R.id.loading).visibility = chipState.isMidTransfer.visibleIfTrue() // Undo - val undoView = currentChipView.requireViewById<View>(R.id.undo) + val undoView = currentView.requireViewById<View>(R.id.undo) val undoClickListener = chipState.undoClickListener( - this, newChipInfo.routeInfo, newChipInfo.undoCallback, uiEventLogger + this, newInfo.routeInfo, newInfo.undoCallback, uiEventLogger ) undoView.setOnClickListener(undoClickListener) undoView.visibility = (undoClickListener != null).visibleIfTrue() // Failure - currentChipView.requireViewById<View>(R.id.failure_icon).visibility = + currentView.requireViewById<View>(R.id.failure_icon).visibility = chipState.isTransferFailure.visibleIfTrue() // For accessibility - currentChipView.requireViewById<ViewGroup>( + currentView.requireViewById<ViewGroup>( R.id.media_ttt_sender_chip_inner ).contentDescription = "$iconName $chipText" } - override fun animateChipIn(chipView: ViewGroup) { - val chipInnerView = chipView.requireViewById<ViewGroup>(R.id.media_ttt_sender_chip_inner) + override fun animateViewIn(view: ViewGroup) { + val chipInnerView = view.requireViewById<ViewGroup>(R.id.media_ttt_sender_chip_inner) ViewHierarchyAnimator.animateAddition( chipInnerView, ViewHierarchyAnimator.Hotspot.TOP, @@ -165,14 +161,14 @@ class MediaTttChipControllerSender @Inject constructor( ) } - override fun removeChip(removalReason: String) { + override fun removeView(removalReason: String) { // Don't remove the chip if we're mid-transfer since the user should still be able to // see the status of the transfer. (But do remove it if it's finally timed out.) - if (chipInfo?.state?.isMidTransfer == true && - removalReason != MediaTttRemovalReason.REASON_TIMEOUT) { + if (info?.state?.isMidTransfer == true && + removalReason != TemporaryDisplayRemovalReason.REASON_TIMEOUT) { return } - super.removeChip(removalReason) + super.removeView(removalReason) } private fun Boolean.visibleIfTrue(): Int { @@ -188,7 +184,7 @@ data class ChipSenderInfo( val state: ChipStateSender, val routeInfo: MediaRoute2Info, val undoCallback: IUndoMediaTransferCallback? = null -) : ChipInfoCommon { +) : TemporaryViewInfo { override fun getTimeoutMs() = state.timeout } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 91c6a9c892e7..c3b265fec36e 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -117,7 +117,6 @@ import com.android.systemui.biometrics.AuthController; import com.android.systemui.camera.CameraGestureHelper; import com.android.systemui.classifier.Classifier; import com.android.systemui.classifier.FalsingCollector; -import com.android.systemui.controls.dagger.ControlsComponent; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.dagger.qualifiers.Main; @@ -140,7 +139,6 @@ import com.android.systemui.plugins.FalsingManager.FalsingTapListener; import com.android.systemui.plugins.qs.QS; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; -import com.android.systemui.qrcodescanner.controller.QRCodeScannerController; import com.android.systemui.screenrecord.RecordingController; import com.android.systemui.shade.transition.ShadeTransitionController; import com.android.systemui.shared.system.QuickStepContract; @@ -217,7 +215,6 @@ import com.android.systemui.util.LargeScreenUtils; import com.android.systemui.util.ListenerSet; import com.android.systemui.util.Utils; import com.android.systemui.util.time.SystemClock; -import com.android.systemui.wallet.controller.QuickAccessWalletController; import com.android.wm.shell.animation.FlingAnimationUtils; import java.io.PrintWriter; @@ -325,9 +322,6 @@ public final class NotificationPanelViewController extends PanelViewController { private final FragmentService mFragmentService; private final ScrimController mScrimController; private final PrivacyDotViewController mPrivacyDotViewController; - private final QuickAccessWalletController mQuickAccessWalletController; - private final QRCodeScannerController mQRCodeScannerController; - private final ControlsComponent mControlsComponent; private final NotificationRemoteInputManager mRemoteInputManager; private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; @@ -696,8 +690,8 @@ public final class NotificationPanelViewController extends PanelViewController { }; private final CameraGestureHelper mCameraGestureHelper; - private final Provider<KeyguardBottomAreaViewModel> mKeyguardBottomAreaViewModelProvider; - private final Provider<KeyguardBottomAreaInteractor> mKeyguardBottomAreaInteractorProvider; + private final KeyguardBottomAreaViewModel mKeyguardBottomAreaViewModel; + private final KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor; @Inject public NotificationPanelViewController(NotificationPanelView view, @@ -746,8 +740,6 @@ public final class NotificationPanelViewController extends PanelViewController { NavigationModeController navigationModeController, FragmentService fragmentService, ContentResolver contentResolver, - QuickAccessWalletController quickAccessWalletController, - QRCodeScannerController qrCodeScannerController, RecordingController recordingController, LargeScreenShadeHeaderController largeScreenShadeHeaderController, ScreenOffAnimationController screenOffAnimationController, @@ -755,7 +747,6 @@ public final class NotificationPanelViewController extends PanelViewController { PanelExpansionStateManager panelExpansionStateManager, NotificationRemoteInputManager remoteInputManager, Optional<SysUIUnfoldComponent> unfoldComponent, - ControlsComponent controlsComponent, InteractionJankMonitor interactionJankMonitor, QsFrameTranslateController qsFrameTranslateController, SysUiState sysUiState, @@ -768,8 +759,8 @@ public final class NotificationPanelViewController extends PanelViewController { ShadeTransitionController shadeTransitionController, SystemClock systemClock, CameraGestureHelper cameraGestureHelper, - Provider<KeyguardBottomAreaViewModel> keyguardBottomAreaViewModelProvider, - Provider<KeyguardBottomAreaInteractor> keyguardBottomAreaInteractorProvider) { + KeyguardBottomAreaViewModel keyguardBottomAreaViewModel, + KeyguardBottomAreaInteractor keyguardBottomAreaInteractor) { super(view, falsingManager, dozeLog, @@ -791,9 +782,6 @@ public final class NotificationPanelViewController extends PanelViewController { mVibratorHelper = vibratorHelper; mKeyguardMediaController = keyguardMediaController; mPrivacyDotViewController = privacyDotViewController; - mQuickAccessWalletController = quickAccessWalletController; - mQRCodeScannerController = qrCodeScannerController; - mControlsComponent = controlsComponent; mMetricsLogger = metricsLogger; mConfigurationController = configurationController; mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder; @@ -897,7 +885,7 @@ public final class NotificationPanelViewController extends PanelViewController { mQsFrameTranslateController = qsFrameTranslateController; updateUserSwitcherFlags(); - mKeyguardBottomAreaViewModelProvider = keyguardBottomAreaViewModelProvider; + mKeyguardBottomAreaViewModel = keyguardBottomAreaViewModel; onFinishInflate(); keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener( new KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener() { @@ -951,7 +939,7 @@ public final class NotificationPanelViewController extends PanelViewController { } }); mCameraGestureHelper = cameraGestureHelper; - mKeyguardBottomAreaInteractorProvider = keyguardBottomAreaInteractorProvider; + mKeyguardBottomAreaInteractor = keyguardBottomAreaInteractor; } @VisibleForTesting @@ -1276,17 +1264,7 @@ public final class NotificationPanelViewController extends PanelViewController { } private void initBottomArea() { - if (mFeatureFlags.isEnabled(Flags.MODERN_BOTTOM_AREA)) { - mKeyguardBottomArea.init(mKeyguardBottomAreaViewModelProvider.get(), mFalsingManager); - } else { - // TODO(b/235403546): remove this method call when the new implementation is complete - // and these are not needed. - mKeyguardBottomArea.init( - mFalsingManager, - mQuickAccessWalletController, - mControlsComponent, - mQRCodeScannerController); - } + mKeyguardBottomArea.init(mKeyguardBottomAreaViewModel, mFalsingManager); } @VisibleForTesting @@ -1403,7 +1381,6 @@ public final class NotificationPanelViewController extends PanelViewController { } mNotificationStackScrollLayoutController.setIntrinsicPadding(stackScrollerPadding); - mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX); mStackScrollerMeasuringPass++; requestScrollerTopPaddingUpdate(animate); @@ -1453,7 +1430,7 @@ public final class NotificationPanelViewController extends PanelViewController { mKeyguardStatusViewController.getClockBottom(mStatusBarHeaderHeightKeyguard), mKeyguardStatusViewController.isClockTopAligned()); mClockPositionAlgorithm.run(mClockPositionResult); - mKeyguardBottomAreaInteractorProvider.get().setClockPosition( + mKeyguardBottomAreaInteractor.setClockPosition( mClockPositionResult.clockX, mClockPositionResult.clockY); boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending(); boolean animateClock = (animate || mAnimateNextPositionUpdate) && shouldAnimateClockChange; @@ -3296,8 +3273,7 @@ public final class NotificationPanelViewController extends PanelViewController { getExpandedFraction()); float alpha = Math.min(expansionAlpha, 1 - computeQsExpansionFraction()); alpha *= mBottomAreaShadeAlpha; - mKeyguardBottomArea.setComponentAlphas(alpha); - mKeyguardBottomAreaInteractorProvider.get().setAlpha(alpha); + mKeyguardBottomAreaInteractor.setAlpha(alpha); mLockIconViewController.setAlpha(alpha); } @@ -3496,8 +3472,7 @@ public final class NotificationPanelViewController extends PanelViewController { } private void updateDozingVisibilities(boolean animate) { - mKeyguardBottomArea.setDozing(mDozing, animate); - mKeyguardBottomAreaInteractorProvider.get().setAnimateDozingTransitions(animate); + mKeyguardBottomAreaInteractor.setAnimateDozingTransitions(animate); if (!mDozing && animate) { mKeyguardStatusBarViewController.animateKeyguardStatusBarIn(); } @@ -3799,8 +3774,7 @@ public final class NotificationPanelViewController extends PanelViewController { mView.setDozing(dozing); mDozing = dozing; mNotificationStackScrollLayoutController.setDozing(mDozing, animate); - mKeyguardBottomArea.setDozing(mDozing, animate); - mKeyguardBottomAreaInteractorProvider.get().setAnimateDozingTransitions(animate); + mKeyguardBottomAreaInteractor.setAnimateDozingTransitions(animate); mKeyguardStatusBarViewController.setDozing(mDozing); if (dozing) { @@ -3849,7 +3823,6 @@ public final class NotificationPanelViewController extends PanelViewController { public void dozeTimeTick() { mLockIconViewController.dozeTimeTick(); - mKeyguardBottomArea.dozeTimeTick(); mKeyguardStatusViewController.dozeTimeTick(); if (mInterpolatedDarkAmount > 0) { positionClockAndNotifications(); @@ -4672,7 +4645,6 @@ public final class NotificationPanelViewController extends PanelViewController { public void onDozeAmountChanged(float linearAmount, float amount) { mInterpolatedDarkAmount = amount; mLinearDarkAmount = linearAmount; - mKeyguardBottomArea.setDarkAmount(mInterpolatedDarkAmount); positionClockAndNotifications(); } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt index 621a6090f4a0..9b3fe92f9225 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt @@ -26,6 +26,7 @@ import com.android.systemui.Dumpable import com.android.systemui.dock.DockManager import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.FalsingManager +import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent import com.android.systemui.tuner.TunerService @@ -49,6 +50,7 @@ class PulsingGestureListener @Inject constructor( private val dockManager: DockManager, private val centralSurfaces: CentralSurfaces, private val ambientDisplayConfiguration: AmbientDisplayConfiguration, + private val statusBarStateController: StatusBarStateController, tunerService: TunerService, dumpManager: DumpManager ) : GestureDetector.SimpleOnGestureListener(), Dumpable { @@ -74,7 +76,8 @@ class PulsingGestureListener @Inject constructor( } override fun onSingleTapConfirmed(e: MotionEvent): Boolean { - if (singleTapEnabled && + if (statusBarStateController.isPulsing && + singleTapEnabled && !dockManager.isDocked && !falsingManager.isProximityNear && !falsingManager.isFalseTap(FalsingManager.MODERATE_PENALTY) @@ -89,7 +92,8 @@ class PulsingGestureListener @Inject constructor( } override fun onDoubleTap(e: MotionEvent): Boolean { - if ((doubleTapEnabled || singleTapEnabled) && + if (statusBarStateController.isPulsing && + (doubleTapEnabled || singleTapEnabled) && !falsingManager.isProximityNear && !falsingManager.isFalseDoubleTap ) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index cf5b2d1ae621..408c61f8f387 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -692,11 +692,11 @@ public class KeyguardIndicationController { /** * Returns the indication text indicating that trust has been granted. * - * @return {@code null} or an empty string if a trust indication text should not be shown. + * @return an empty string if a trust indication text should not be shown. */ @VisibleForTesting String getTrustGrantedIndication() { - return TextUtils.isEmpty(mTrustGrantedIndication) + return mTrustGrantedIndication == null ? mContext.getString(R.string.keyguard_indication_trust_unlocked) : mTrustGrantedIndication.toString(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 456d4cf4588b..4cc67a0f5e79 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -3306,14 +3306,12 @@ public class CentralSurfacesImpl extends CoreStartable implements // show the bouncer/lockscreen. if (!mKeyguardViewMediator.isHiding() && !mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) { - if (mState == StatusBarState.SHADE_LOCKED - && mKeyguardUpdateMonitor.isUdfpsEnrolled()) { + if (mState == StatusBarState.SHADE_LOCKED) { // shade is showing while locked on the keyguard, so go back to showing the // lock screen where users can use the UDFPS affordance to enter the device mStatusBarKeyguardViewManager.reset(true); - } else if ((mState == StatusBarState.KEYGUARD - && !mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()) - || mState == StatusBarState.SHADE_LOCKED) { + } else if (mState == StatusBarState.KEYGUARD + && !mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()) { mStatusBarKeyguardViewManager.showGenericBouncer(true /* scrimmed */); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java deleted file mode 100644 index cc451d9ef0cd..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ /dev/null @@ -1,682 +0,0 @@ -/* - * Copyright (C) 2014 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.statusbar.phone; - -import static com.android.internal.util.Preconditions.checkNotNull; -import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE; -import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; -import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE; -import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE; - -import android.app.ActivityTaskManager; -import android.app.admin.DevicePolicyManager; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.res.ColorStateList; -import android.content.res.Configuration; -import android.graphics.drawable.Drawable; -import android.os.RemoteException; -import android.os.UserHandle; -import android.service.quickaccesswallet.GetWalletCardsError; -import android.service.quickaccesswallet.GetWalletCardsResponse; -import android.service.quickaccesswallet.QuickAccessWalletClient; -import android.util.AttributeSet; -import android.util.Log; -import android.util.TypedValue; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewPropertyAnimator; -import android.view.WindowInsets; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.settingslib.Utils; -import com.android.systemui.Dependency; -import com.android.systemui.R; -import com.android.systemui.animation.ActivityLaunchAnimator; -import com.android.systemui.animation.Interpolators; -import com.android.systemui.controls.dagger.ControlsComponent; -import com.android.systemui.controls.management.ControlsListingController; -import com.android.systemui.controls.ui.ControlsActivity; -import com.android.systemui.controls.ui.ControlsUiController; -import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder; -import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel; -import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.qrcodescanner.controller.QRCodeScannerController; -import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.wallet.controller.QuickAccessWalletController; - -import java.util.ArrayList; -import java.util.List; - -/** - * Implementation for the bottom area of the Keyguard, including camera/phone affordance and status - * text. - */ -public class KeyguardBottomAreaView extends FrameLayout { - - private static final String TAG = "CentralSurfaces/KeyguardBottomAreaView"; - private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250; - - private ImageView mWalletButton; - private ImageView mQRCodeScannerButton; - private ImageView mControlsButton; - private boolean mHasCard = false; - private final WalletCardRetriever mCardRetriever = new WalletCardRetriever(); - private QuickAccessWalletController mQuickAccessWalletController; - private QRCodeScannerController mQRCodeScannerController; - private ControlsComponent mControlsComponent; - private boolean mControlServicesAvailable = false; - - @Nullable private View mAmbientIndicationArea; - private ViewGroup mIndicationArea; - private TextView mIndicationText; - private TextView mIndicationTextBottom; - private ViewGroup mOverlayContainer; - - private ActivityStarter mActivityStarter; - private KeyguardStateController mKeyguardStateController; - private FalsingManager mFalsingManager; - - private boolean mDozing; - private int mIndicationBottomMargin; - private int mIndicationPadding; - private float mDarkAmount; - private int mBurnInXOffset; - private int mBurnInYOffset; - - private final ControlsListingController.ControlsListingCallback mListingCallback = - serviceInfos -> post(() -> { - boolean available = !serviceInfos.isEmpty(); - - if (available != mControlServicesAvailable) { - mControlServicesAvailable = available; - updateControlsVisibility(); - updateAffordanceColors(); - } - }); - - private final KeyguardStateController.Callback mKeyguardStateCallback = - new KeyguardStateController.Callback() { - @Override - public void onKeyguardShowingChanged() { - if (mKeyguardStateController.isShowing()) { - if (mQuickAccessWalletController != null) { - mQuickAccessWalletController.queryWalletCards(mCardRetriever); - } - } - } - }; - - @Nullable private KeyguardBottomAreaViewBinder.Binding mBinding; - private boolean mUsesBinder; - - public KeyguardBottomAreaView(Context context) { - this(context, null); - } - - public KeyguardBottomAreaView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr, - int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - /** - * Initializes the view. - */ - public void init( - final KeyguardBottomAreaViewModel viewModel, - final FalsingManager falsingManager) { - Log.i(TAG, System.identityHashCode(this) + " initialized with a binder"); - mUsesBinder = true; - mBinding = KeyguardBottomAreaViewBinder.bind(this, viewModel, falsingManager); - } - - /** - * Initializes the {@link KeyguardBottomAreaView} with the given dependencies - * - * @deprecated Use - * {@link #init(KeyguardBottomAreaViewModel, FalsingManager)} instead - */ - @Deprecated - public void init( - FalsingManager falsingManager, - QuickAccessWalletController controller, - ControlsComponent controlsComponent, - QRCodeScannerController qrCodeScannerController) { - if (mUsesBinder) { - return; - } - - Log.i(TAG, "initialized without a binder"); - mFalsingManager = falsingManager; - - mQuickAccessWalletController = controller; - mQuickAccessWalletController.setupWalletChangeObservers( - mCardRetriever, WALLET_PREFERENCE_CHANGE, DEFAULT_PAYMENT_APP_CHANGE); - mQuickAccessWalletController.updateWalletPreference(); - mQuickAccessWalletController.queryWalletCards(mCardRetriever); - updateWalletVisibility(); - - mControlsComponent = controlsComponent; - mControlsComponent.getControlsListingController().ifPresent( - c -> c.addCallback(mListingCallback)); - - mQRCodeScannerController = qrCodeScannerController; - mQRCodeScannerController.registerQRCodeScannerChangeObservers( - QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE, - QRCodeScannerController.QR_CODE_SCANNER_PREFERENCE_CHANGE); - updateQRCodeButtonVisibility(); - - updateAffordanceColors(); - } - - /** - * Initializes this instance of {@link KeyguardBottomAreaView} based on the given instance of - * another {@link KeyguardBottomAreaView} - */ - public void initFrom(KeyguardBottomAreaView oldBottomArea) { - if (mUsesBinder) { - return; - } - - // if it exists, continue to use the original ambient indication container - // instead of the newly inflated one - if (mAmbientIndicationArea != null) { - // remove old ambient indication from its parent - View originalAmbientIndicationView = - oldBottomArea.findViewById(R.id.ambient_indication_container); - ((ViewGroup) originalAmbientIndicationView.getParent()) - .removeView(originalAmbientIndicationView); - - // remove current ambient indication from its parent (discard) - ViewGroup ambientIndicationParent = (ViewGroup) mAmbientIndicationArea.getParent(); - int ambientIndicationIndex = - ambientIndicationParent.indexOfChild(mAmbientIndicationArea); - ambientIndicationParent.removeView(mAmbientIndicationArea); - - // add the old ambient indication to this view - ambientIndicationParent.addView(originalAmbientIndicationView, ambientIndicationIndex); - mAmbientIndicationArea = originalAmbientIndicationView; - - // update burn-in offsets - dozeTimeTick(); - } - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - if (mUsesBinder) { - return; - } - - mOverlayContainer = findViewById(R.id.overlay_container); - mWalletButton = findViewById(R.id.wallet_button); - mQRCodeScannerButton = findViewById(R.id.qr_code_scanner_button); - mControlsButton = findViewById(R.id.controls_button); - mIndicationArea = findViewById(R.id.keyguard_indication_area); - mAmbientIndicationArea = findViewById(R.id.ambient_indication_container); - mIndicationText = findViewById(R.id.keyguard_indication_text); - mIndicationTextBottom = findViewById(R.id.keyguard_indication_text_bottom); - mIndicationBottomMargin = getResources().getDimensionPixelSize( - R.dimen.keyguard_indication_margin_bottom); - mBurnInYOffset = getResources().getDimensionPixelSize( - R.dimen.default_burn_in_prevention_offset); - mKeyguardStateController = Dependency.get(KeyguardStateController.class); - mKeyguardStateController.addCallback(mKeyguardStateCallback); - setClipChildren(false); - setClipToPadding(false); - mActivityStarter = Dependency.get(ActivityStarter.class); - - mIndicationPadding = getResources().getDimensionPixelSize( - R.dimen.keyguard_indication_area_padding); - updateWalletVisibility(); - updateQRCodeButtonVisibility(); - updateControlsVisibility(); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - if (mUsesBinder) { - return; - } - - final IntentFilter filter = new IntentFilter(); - filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); - mKeyguardStateController.addCallback(mKeyguardStateCallback); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - if (mUsesBinder) { - return; - } - - mKeyguardStateController.removeCallback(mKeyguardStateCallback); - - if (mQuickAccessWalletController != null) { - mQuickAccessWalletController.unregisterWalletChangeObservers( - WALLET_PREFERENCE_CHANGE, DEFAULT_PAYMENT_APP_CHANGE); - } - - if (mQRCodeScannerController != null) { - mQRCodeScannerController.unregisterQRCodeScannerChangeObservers( - QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE, - QRCodeScannerController.QR_CODE_SCANNER_PREFERENCE_CHANGE); - } - - if (mControlsComponent != null) { - mControlsComponent.getControlsListingController().ifPresent( - c -> c.removeCallback(mListingCallback)); - } - } - - @Override - protected void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - if (mUsesBinder) { - if (mBinding != null) { - mBinding.onConfigurationChanged(); - } - return; - } - - mIndicationBottomMargin = getResources().getDimensionPixelSize( - R.dimen.keyguard_indication_margin_bottom); - mBurnInYOffset = getResources().getDimensionPixelSize( - R.dimen.default_burn_in_prevention_offset); - MarginLayoutParams mlp = (MarginLayoutParams) mIndicationArea.getLayoutParams(); - if (mlp.bottomMargin != mIndicationBottomMargin) { - mlp.bottomMargin = mIndicationBottomMargin; - mIndicationArea.setLayoutParams(mlp); - } - - // Respect font size setting. - mIndicationTextBottom.setTextSize(TypedValue.COMPLEX_UNIT_PX, - getResources().getDimensionPixelSize( - com.android.internal.R.dimen.text_size_small_material)); - mIndicationText.setTextSize(TypedValue.COMPLEX_UNIT_PX, - getResources().getDimensionPixelSize( - com.android.internal.R.dimen.text_size_small_material)); - - ViewGroup.LayoutParams lp = mWalletButton.getLayoutParams(); - lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width); - lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height); - mWalletButton.setLayoutParams(lp); - - lp = mQRCodeScannerButton.getLayoutParams(); - lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width); - lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height); - mQRCodeScannerButton.setLayoutParams(lp); - - lp = mControlsButton.getLayoutParams(); - lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width); - lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height); - mControlsButton.setLayoutParams(lp); - - mIndicationPadding = getResources().getDimensionPixelSize( - R.dimen.keyguard_indication_area_padding); - - updateWalletVisibility(); - updateQRCodeButtonVisibility(); - updateAffordanceColors(); - } - - private void updateWalletVisibility() { - if (mUsesBinder) { - return; - } - - if (mDozing - || mQuickAccessWalletController == null - || !mQuickAccessWalletController.isWalletEnabled() - || !mHasCard) { - mWalletButton.setVisibility(GONE); - - if (mControlsButton.getVisibility() == GONE) { - mIndicationArea.setPadding(0, 0, 0, 0); - } - } else { - mWalletButton.setVisibility(VISIBLE); - mWalletButton.setOnClickListener(this::onWalletClick); - mIndicationArea.setPadding(mIndicationPadding, 0, mIndicationPadding, 0); - } - } - - private void updateControlsVisibility() { - if (mUsesBinder) { - return; - } - - if (mControlsComponent == null) return; - - mControlsButton.setImageResource(mControlsComponent.getTileImageId()); - mControlsButton.setContentDescription(getContext() - .getString(mControlsComponent.getTileTitleId())); - updateAffordanceColors(); - - boolean hasFavorites = mControlsComponent.getControlsController() - .map(c -> c.getFavorites().size() > 0) - .orElse(false); - if (mDozing - || !hasFavorites - || !mControlServicesAvailable - || mControlsComponent.getVisibility() != AVAILABLE) { - mControlsButton.setVisibility(GONE); - if (mWalletButton.getVisibility() == GONE) { - mIndicationArea.setPadding(0, 0, 0, 0); - } - } else { - mControlsButton.setVisibility(VISIBLE); - mControlsButton.setOnClickListener(this::onControlsClick); - mIndicationArea.setPadding(mIndicationPadding, 0, mIndicationPadding, 0); - } - } - - public void setDarkAmount(float darkAmount) { - if (mUsesBinder) { - return; - } - - if (darkAmount == mDarkAmount) { - return; - } - mDarkAmount = darkAmount; - dozeTimeTick(); - } - - /** - * Returns a list of animators to use to animate the indication areas. - */ - public List<ViewPropertyAnimator> getIndicationAreaAnimators() { - if (mUsesBinder) { - return checkNotNull(mBinding).getIndicationAreaAnimators(); - } - - List<ViewPropertyAnimator> animators = - new ArrayList<>(mAmbientIndicationArea != null ? 2 : 1); - animators.add(mIndicationArea.animate()); - if (mAmbientIndicationArea != null) { - animators.add(mAmbientIndicationArea.animate()); - } - return animators; - } - - @Override - public boolean hasOverlappingRendering() { - return false; - } - - private void startFinishDozeAnimation() { - long delay = 0; - if (mWalletButton.getVisibility() == View.VISIBLE) { - startFinishDozeAnimationElement(mWalletButton, delay); - } - if (mQRCodeScannerButton.getVisibility() == View.VISIBLE) { - startFinishDozeAnimationElement(mQRCodeScannerButton, delay); - } - if (mControlsButton.getVisibility() == View.VISIBLE) { - startFinishDozeAnimationElement(mControlsButton, delay); - } - } - - private void startFinishDozeAnimationElement(View element, long delay) { - element.setAlpha(0f); - element.setTranslationY(element.getHeight() / 2); - element.animate() - .alpha(1f) - .translationY(0f) - .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN) - .setStartDelay(delay) - .setDuration(DOZE_ANIMATION_ELEMENT_DURATION); - } - - public void setDozing(boolean dozing, boolean animate) { - if (mUsesBinder) { - return; - } - - mDozing = dozing; - - updateWalletVisibility(); - updateControlsVisibility(); - updateQRCodeButtonVisibility(); - - if (dozing) { - mOverlayContainer.setVisibility(INVISIBLE); - } else { - mOverlayContainer.setVisibility(VISIBLE); - if (animate) { - startFinishDozeAnimation(); - } - } - } - - public void dozeTimeTick() { - if (mUsesBinder) { - return; - } - - int burnInYOffset = getBurnInOffset(mBurnInYOffset * 2, false /* xAxis */) - - mBurnInYOffset; - mIndicationArea.setTranslationY(burnInYOffset * mDarkAmount); - if (mAmbientIndicationArea != null) { - mAmbientIndicationArea.setTranslationY(burnInYOffset * mDarkAmount); - } - } - - public void setAntiBurnInOffsetX(int burnInXOffset) { - if (mUsesBinder) { - return; - } - - if (mBurnInXOffset == burnInXOffset) { - return; - } - mBurnInXOffset = burnInXOffset; - mIndicationArea.setTranslationX(burnInXOffset); - if (mAmbientIndicationArea != null) { - mAmbientIndicationArea.setTranslationX(burnInXOffset); - } - } - - /** - * Sets the alpha of various sub-components, for example the indication areas and bottom quick - * action buttons. Does not set the alpha of the lock icon. - */ - public void setComponentAlphas(float alpha) { - if (mUsesBinder) { - return; - } - - setImportantForAccessibility( - alpha == 0f - ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS - : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); - if (mAmbientIndicationArea != null) { - mAmbientIndicationArea.setAlpha(alpha); - } - mIndicationArea.setAlpha(alpha); - mWalletButton.setAlpha(alpha); - mQRCodeScannerButton.setAlpha(alpha); - mControlsButton.setAlpha(alpha); - } - - @Override - public WindowInsets onApplyWindowInsets(WindowInsets insets) { - int bottom = insets.getDisplayCutout() != null - ? insets.getDisplayCutout().getSafeInsetBottom() : 0; - if (isPaddingRelative()) { - setPaddingRelative(getPaddingStart(), getPaddingTop(), getPaddingEnd(), bottom); - } else { - setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), bottom); - } - return insets; - } - - private void updateQRCodeButtonVisibility() { - if (mUsesBinder) { - return; - } - - if (mQuickAccessWalletController != null - && mQuickAccessWalletController.isWalletEnabled()) { - // Don't enable if quick access wallet is enabled - return; - } - - if (mQRCodeScannerController != null - && mQRCodeScannerController.isEnabledForLockScreenButton()) { - mQRCodeScannerButton.setVisibility(VISIBLE); - mQRCodeScannerButton.setOnClickListener(this::onQRCodeScannerClicked); - mIndicationArea.setPadding(mIndicationPadding, 0, mIndicationPadding, 0); - } else { - mQRCodeScannerButton.setVisibility(GONE); - if (mControlsButton.getVisibility() == GONE) { - mIndicationArea.setPadding(0, 0, 0, 0); - } - } - } - - private void onQRCodeScannerClicked(View view) { - if (mUsesBinder) { - return; - } - - Intent intent = mQRCodeScannerController.getIntent(); - if (intent != null) { - try { - ActivityTaskManager.getService().startActivityAsUser( - null, getContext().getBasePackageName(), - getContext().getAttributionTag(), intent, - intent.resolveTypeIfNeeded(getContext().getContentResolver()), - null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, null, - UserHandle.CURRENT.getIdentifier()); - } catch (RemoteException e) { - // This is unexpected. Nonetheless, just log the error and prevent the UI from - // crashing - Log.e(TAG, "Unexpected intent: " + intent - + " when the QR code scanner button was clicked"); - } - } - } - - private void updateAffordanceColors() { - if (mUsesBinder) { - return; - } - - int iconColor = Utils.getColorAttrDefaultColor( - mContext, - com.android.internal.R.attr.textColorPrimary); - mWalletButton.getDrawable().setTint(iconColor); - mControlsButton.getDrawable().setTint(iconColor); - mQRCodeScannerButton.getDrawable().setTint(iconColor); - - ColorStateList bgColor = Utils.getColorAttr( - mContext, - com.android.internal.R.attr.colorSurface); - mWalletButton.setBackgroundTintList(bgColor); - mControlsButton.setBackgroundTintList(bgColor); - mQRCodeScannerButton.setBackgroundTintList(bgColor); - } - - private void onWalletClick(View v) { - if (mUsesBinder) { - return; - } - - // More coming here; need to inform the user about how to proceed - if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { - return; - } - - ActivityLaunchAnimator.Controller animationController = createLaunchAnimationController(v); - mQuickAccessWalletController.startQuickAccessUiIntent( - mActivityStarter, animationController, mHasCard); - } - - protected ActivityLaunchAnimator.Controller createLaunchAnimationController(View view) { - return ActivityLaunchAnimator.Controller.fromView(view, null); - } - - private void onControlsClick(View v) { - if (mUsesBinder) { - return; - } - - if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { - return; - } - - Intent intent = new Intent(mContext, ControlsActivity.class) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK) - .putExtra(ControlsUiController.EXTRA_ANIMATE, true); - - ActivityLaunchAnimator.Controller controller = - v != null ? ActivityLaunchAnimator.Controller.fromView(v, null /* cujType */) - : null; - if (mControlsComponent.getVisibility() == AVAILABLE) { - mActivityStarter.startActivity(intent, true /* dismissShade */, controller, - true /* showOverLockscreenWhenLocked */); - } else { - mActivityStarter.postStartActivityDismissingKeyguard(intent, 0 /* delay */, controller); - } - } - - private class WalletCardRetriever implements - QuickAccessWalletClient.OnWalletCardsRetrievedCallback { - - @Override - public void onWalletCardsRetrieved(@NonNull GetWalletCardsResponse response) { - mHasCard = !response.getWalletCards().isEmpty(); - Drawable tileIcon = mQuickAccessWalletController.getWalletClient().getTileIcon(); - post(() -> { - if (tileIcon != null) { - mWalletButton.setImageDrawable(tileIcon); - } - updateWalletVisibility(); - updateAffordanceColors(); - }); - } - - @Override - public void onWalletCardRetrievalError(@NonNull GetWalletCardsError error) { - mHasCard = false; - post(() -> { - updateWalletVisibility(); - updateAffordanceColors(); - }); - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt new file mode 100644 index 000000000000..4897c529dd51 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2014 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.statusbar.phone + +import android.content.Context +import android.content.res.Configuration +import android.util.AttributeSet +import android.view.View +import android.view.ViewGroup +import android.view.ViewPropertyAnimator +import android.view.WindowInsets +import android.widget.FrameLayout +import com.android.systemui.R +import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder +import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder.bind +import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel +import com.android.systemui.plugins.FalsingManager + +/** + * Renders the bottom area of the lock-screen. Concerned primarily with the quick affordance UI + * elements. A secondary concern is the interaction of the quick affordance elements with the + * indication area between them, though the indication area is primarily controlled elsewhere. + */ +class KeyguardBottomAreaView +@JvmOverloads +constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, + defStyleRes: Int = 0, +) : + FrameLayout( + context, + attrs, + defStyleAttr, + defStyleRes, + ) { + + private var ambientIndicationArea: View? = null + private lateinit var binding: KeyguardBottomAreaViewBinder.Binding + + /** Initializes the view. */ + fun init( + viewModel: KeyguardBottomAreaViewModel, + falsingManager: FalsingManager, + ) { + binding = bind(this, viewModel, falsingManager) + } + + /** + * Initializes this instance of [KeyguardBottomAreaView] based on the given instance of another + * [KeyguardBottomAreaView] + */ + fun initFrom(oldBottomArea: KeyguardBottomAreaView) { + // if it exists, continue to use the original ambient indication container + // instead of the newly inflated one + ambientIndicationArea?.let { nonNullAmbientIndicationArea -> + // remove old ambient indication from its parent + val originalAmbientIndicationView = + oldBottomArea.findViewById<View>(R.id.ambient_indication_container) + (originalAmbientIndicationView.parent as ViewGroup).removeView( + originalAmbientIndicationView + ) + + // remove current ambient indication from its parent (discard) + val ambientIndicationParent = nonNullAmbientIndicationArea.parent as ViewGroup + val ambientIndicationIndex = + ambientIndicationParent.indexOfChild(nonNullAmbientIndicationArea) + ambientIndicationParent.removeView(nonNullAmbientIndicationArea) + + // add the old ambient indication to this view + ambientIndicationParent.addView(originalAmbientIndicationView, ambientIndicationIndex) + ambientIndicationArea = originalAmbientIndicationView + } + } + + override fun onFinishInflate() { + super.onFinishInflate() + ambientIndicationArea = findViewById(R.id.ambient_indication_container) + } + + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + binding.onConfigurationChanged() + } + + /** Returns a list of animators to use to animate the indication areas. */ + val indicationAreaAnimators: List<ViewPropertyAnimator> + get() = binding.getIndicationAreaAnimators() + + override fun hasOverlappingRendering(): Boolean { + return false + } + + override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets { + val bottom = insets.displayCutout?.safeInsetBottom ?: 0 + if (isPaddingRelative) { + setPaddingRelative(paddingStart, paddingTop, paddingEnd, bottom) + } else { + setPadding(paddingLeft, paddingTop, paddingRight, bottom) + } + return insets + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 75e2c5390b8e..e106f811574c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -378,11 +378,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb return; } else if (mNotificationPanelViewController.isUnlockHintRunning()) { mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); - } else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED - && mKeyguardUpdateManager.isUdfpsEnrolled()) { + } else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) { // Don't expand to the bouncer. Instead transition back to the lock screen (see - // CentralSurfaces#showBouncerOrLockScreenIfKeyguard) where the user can use the UDFPS - // affordance to enter the device (or swipe up to the input bouncer) + // CentralSurfaces#showBouncerOrLockScreenIfKeyguard) return; } else if (bouncerNeedsScrimming()) { mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java index bdac88837969..f4d08e01d5c3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java @@ -31,6 +31,7 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.keyguard.logging.KeyguardUpdateMonitorLogger; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; @@ -60,6 +61,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback = new UpdateMonitorCallback(); private final Lazy<KeyguardUnlockAnimationController> mUnlockAnimationControllerLazy; + private final KeyguardUpdateMonitorLogger mLogger; private boolean mCanDismissLockScreen; private boolean mShowing; @@ -107,8 +109,10 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils, Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationController, + KeyguardUpdateMonitorLogger logger, DumpManager dumpManager) { mContext = context; + mLogger = logger; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mLockPatternUtils = lockPatternUtils; mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback); @@ -245,6 +249,8 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum mTrusted = trusted; mTrustManaged = trustManaged; mFaceAuthEnabled = faceAuthEnabled; + mLogger.logKeyguardStateUpdate( + mSecure, mCanDismissLockScreen, mTrusted, mTrustManaged); notifyUnlockedChanged(); } Trace.endSection(); diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt index 3a0ac1b7d9b0..734eeecec215 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt +++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.media.taptotransfer.common +package com.android.systemui.temporarydisplay import android.annotation.LayoutRes import android.annotation.SuppressLint @@ -37,30 +37,30 @@ import com.android.internal.widget.CachingIconView import com.android.settingslib.Utils import com.android.systemui.R import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.media.taptotransfer.common.MediaTttLogger import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.concurrency.DelayableExecutor -import com.android.systemui.util.view.ViewUtil /** - * A superclass controller that provides common functionality for showing chips on the sender device - * and the receiver device. + * A generic controller that can temporarily display a new view in a new window. * - * Subclasses need to override and implement [updateChipView], which is where they can control what + * Subclasses need to override and implement [updateView], which is where they can control what * gets displayed to the user. * * The generic type T is expected to contain all the information necessary for the subclasses to - * display the chip in a certain state, since they receive <T> in [updateChipView]. + * display the view in a certain state, since they receive <T> in [updateView]. + * + * TODO(b/245610654): Remove all the media-specific logic from this class. */ -abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>( - internal val context: Context, - internal val logger: MediaTttLogger, - internal val windowManager: WindowManager, - private val viewUtil: ViewUtil, - @Main private val mainExecutor: DelayableExecutor, - private val accessibilityManager: AccessibilityManager, - private val configurationController: ConfigurationController, - private val powerManager: PowerManager, - @LayoutRes private val chipLayoutRes: Int, +abstract class TemporaryViewDisplayController<T : TemporaryViewInfo>( + internal val context: Context, + internal val logger: MediaTttLogger, + internal val windowManager: WindowManager, + @Main private val mainExecutor: DelayableExecutor, + private val accessibilityManager: AccessibilityManager, + private val configurationController: ConfigurationController, + private val powerManager: PowerManager, + @LayoutRes private val viewLayoutRes: Int, ) { /** * Window layout params that will be used as a starting point for the [windowLayoutParams] of @@ -85,31 +85,31 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>( */ internal abstract val windowLayoutParams: WindowManager.LayoutParams - /** The chip view currently being displayed. Null if the chip is not being displayed. */ - private var chipView: ViewGroup? = null + /** The view currently being displayed. Null if the view is not being displayed. */ + private var view: ViewGroup? = null - /** The chip info currently being displayed. Null if the chip is not being displayed. */ - internal var chipInfo: T? = null + /** The info currently being displayed. Null if the view is not being displayed. */ + internal var info: T? = null - /** A [Runnable] that, when run, will cancel the pending timeout of the chip. */ - private var cancelChipViewTimeout: Runnable? = null + /** A [Runnable] that, when run, will cancel the pending timeout of the view. */ + private var cancelViewTimeout: Runnable? = null /** - * Displays the chip with the provided [newChipInfo]. + * Displays the view with the provided [newInfo]. * - * This method handles inflating and attaching the view, then delegates to [updateChipView] to - * display the correct information in the chip. + * This method handles inflating and attaching the view, then delegates to [updateView] to + * display the correct information in the view. */ - fun displayChip(newChipInfo: T) { - val currentChipView = chipView + fun displayView(newInfo: T) { + val currentView = view - if (currentChipView != null) { - updateChipView(newChipInfo, currentChipView) + if (currentView != null) { + updateView(newInfo, currentView) } else { - // The chip is new, so set up all our callbacks and inflate the view + // The view is new, so set up all our callbacks and inflate the view configurationController.addCallback(displayScaleListener) - // Wake the screen if necessary so the user will see the chip. (Per b/239426653, we want - // the chip to show over the dream state, so we should only wake up if the screen is + // Wake the screen if necessary so the user will see the view. (Per b/239426653, we want + // the view to show over the dream state, so we should only wake up if the screen is // completely off.) if (!powerManager.isScreenOn) { powerManager.wakeUp( @@ -119,79 +119,79 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>( ) } - inflateAndUpdateChip(newChipInfo) + inflateAndUpdateView(newInfo) } - // Cancel and re-set the chip timeout each time we get a new state. + // Cancel and re-set the view timeout each time we get a new state. val timeout = accessibilityManager.getRecommendedTimeoutMillis( - newChipInfo.getTimeoutMs().toInt(), - // Not all chips have controls so FLAG_CONTENT_CONTROLS might be superfluous, but + newInfo.getTimeoutMs().toInt(), + // Not all views have controls so FLAG_CONTENT_CONTROLS might be superfluous, but // include it just to be safe. FLAG_CONTENT_ICONS or FLAG_CONTENT_TEXT or FLAG_CONTENT_CONTROLS ) - cancelChipViewTimeout?.run() - cancelChipViewTimeout = mainExecutor.executeDelayed( - { removeChip(MediaTttRemovalReason.REASON_TIMEOUT) }, + cancelViewTimeout?.run() + cancelViewTimeout = mainExecutor.executeDelayed( + { removeView(TemporaryDisplayRemovalReason.REASON_TIMEOUT) }, timeout.toLong() ) } - /** Inflates a new chip view, updates it with [newChipInfo], and adds the view to the window. */ - private fun inflateAndUpdateChip(newChipInfo: T) { - val newChipView = LayoutInflater + /** Inflates a new view, updates it with [newInfo], and adds the view to the window. */ + private fun inflateAndUpdateView(newInfo: T) { + val newView = LayoutInflater .from(context) - .inflate(chipLayoutRes, null) as ViewGroup - chipView = newChipView - updateChipView(newChipInfo, newChipView) - windowManager.addView(newChipView, windowLayoutParams) - animateChipIn(newChipView) + .inflate(viewLayoutRes, null) as ViewGroup + view = newView + updateView(newInfo, newView) + windowManager.addView(newView, windowLayoutParams) + animateViewIn(newView) } - /** Removes then re-inflates the chip. */ - private fun reinflateChip() { - val currentChipInfo = chipInfo - if (chipView == null || currentChipInfo == null) { return } + /** Removes then re-inflates the view. */ + private fun reinflateView() { + val currentInfo = info + if (view == null || currentInfo == null) { return } - windowManager.removeView(chipView) - inflateAndUpdateChip(currentChipInfo) + windowManager.removeView(view) + inflateAndUpdateView(currentInfo) } private val displayScaleListener = object : ConfigurationController.ConfigurationListener { override fun onDensityOrFontScaleChanged() { - reinflateChip() + reinflateView() } } /** - * Hides the chip. + * Hides the view. * - * @param removalReason a short string describing why the chip was removed (timeout, state + * @param removalReason a short string describing why the view was removed (timeout, state * change, etc.) */ - open fun removeChip(removalReason: String) { - if (chipView == null) { return } + open fun removeView(removalReason: String) { + if (view == null) { return } logger.logChipRemoval(removalReason) configurationController.removeCallback(displayScaleListener) - windowManager.removeView(chipView) - chipView = null - chipInfo = null - // No need to time the chip out since it's already gone - cancelChipViewTimeout?.run() + windowManager.removeView(view) + view = null + info = null + // No need to time the view out since it's already gone + cancelViewTimeout?.run() } /** - * A method implemented by subclasses to update [currentChipView] based on [newChipInfo]. + * A method implemented by subclasses to update [currentView] based on [newInfo]. */ @CallSuper - open fun updateChipView(newChipInfo: T, currentChipView: ViewGroup) { - chipInfo = newChipInfo + open fun updateView(newInfo: T, currentView: ViewGroup) { + info = newInfo } /** - * A method that can be implemented by subclcasses to do custom animations for when the chip + * A method that can be implemented by subclasses to do custom animations for when the view * appears. */ - open fun animateChipIn(chipView: ViewGroup) {} + open fun animateViewIn(view: ViewGroup) {} /** * Returns the size that the icon should be, or null if no size override is needed. @@ -209,12 +209,12 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>( * @return the content description of the icon. */ internal fun setIcon( - currentChipView: ViewGroup, + currentView: ViewGroup, appPackageName: String?, appIconDrawableOverride: Drawable? = null, appNameOverride: CharSequence? = null, ): CharSequence { - val appIconView = currentChipView.requireViewById<CachingIconView>(R.id.app_icon) + val appIconView = currentView.requireViewById<CachingIconView>(R.id.app_icon) val iconInfo = getIconInfo(appPackageName) getIconSize(iconInfo.isAppIcon)?.let { size -> @@ -264,9 +264,9 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>( // Used in CTS tests UpdateMediaTapToTransferSenderDisplayTest and // UpdateMediaTapToTransferReceiverDisplayTest private const val WINDOW_TITLE = "Media Transfer Chip View" -private val TAG = MediaTttChipControllerCommon::class.simpleName!! +private val TAG = TemporaryViewDisplayController::class.simpleName!! -object MediaTttRemovalReason { +object TemporaryDisplayRemovalReason { const val REASON_TIMEOUT = "TIMEOUT" const val REASON_SCREEN_TAP = "SCREEN_TAP" } diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt index a29c5883118c..4fe753a80faf 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt +++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.android.systemui.media.taptotransfer.common +package com.android.systemui.temporarydisplay /** - * A superclass chip state that will be subclassed by the sender chip and receiver chip. + * A superclass view state used with [TemporaryViewDisplayController]. */ -interface ChipInfoCommon { +interface TemporaryViewInfo { /** - * Returns the amount of time the given chip state should display on the screen before it times + * Returns the amount of time the given view state should display on the screen before it times * out and disappears. */ - fun getTimeoutMs(): Long + fun getTimeoutMs(): Long = DEFAULT_TIMEOUT_MILLIS } const val DEFAULT_TIMEOUT_MILLIS = 10000L diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt index 9acd21cc6398..9a91ea91f3a2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt @@ -51,18 +51,19 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes @Parameters( name = "feature enabled = {0}, has favorites = {1}, has service infos = {2}, can show" + - " while locked = {3} - expected visible = {4}" + " while locked = {3}, visibility is AVAILABLE {4} - expected visible = {5}" ) @JvmStatic fun data() = - (0 until 16) + (0 until 32) .map { combination -> arrayOf( - /* isFeatureEnabled= */ combination and 0b1000 != 0, - /* hasFavorites= */ combination and 0b0100 != 0, - /* hasServiceInfos= */ combination and 0b0010 != 0, - /* canShowWhileLocked= */ combination and 0b0001 != 0, - /* isVisible= */ combination == 0b1111, + /* isFeatureEnabled= */ combination and 0b10000 != 0, + /* hasFavorites= */ combination and 0b01000 != 0, + /* hasServiceInfos= */ combination and 0b00100 != 0, + /* canShowWhileLocked= */ combination and 0b00010 != 0, + /* visibilityAvailable= */ combination and 0b00001 != 0, + /* isVisible= */ combination == 0b11111, ) } .toList() @@ -81,7 +82,8 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes @JvmField @Parameter(1) var hasFavorites: Boolean = false @JvmField @Parameter(2) var hasServiceInfos: Boolean = false @JvmField @Parameter(3) var canShowWhileLocked: Boolean = false - @JvmField @Parameter(4) var isVisible: Boolean = false + @JvmField @Parameter(4) var isVisibilityAvailable: Boolean = false + @JvmField @Parameter(5) var isVisibleExpected: Boolean = false @Before fun setUp() { @@ -93,6 +95,14 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes .thenReturn(Optional.of(controlsListingController)) whenever(component.canShowWhileLockedSetting) .thenReturn(MutableStateFlow(canShowWhileLocked)) + whenever(component.getVisibility()) + .thenReturn( + if (isVisibilityAvailable) { + ControlsComponent.Visibility.AVAILABLE + } else { + ControlsComponent.Visibility.UNAVAILABLE + } + ) underTest = HomeControlsKeyguardQuickAffordanceConfig( @@ -128,7 +138,7 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes assertThat(values.last()) .isInstanceOf( - if (isVisible) { + if (isVisibleExpected) { KeyguardQuickAffordanceConfig.State.Visible::class.java } else { KeyguardQuickAffordanceConfig.State.Hidden::class.java diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt index 059487dfdbc8..dede4ec0210c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt @@ -69,6 +69,7 @@ class HomeControlsKeyguardQuickAffordanceConfigTest : SysuiTestCase() { val controlsController = mock<ControlsController>() whenever(component.getControlsController()).thenReturn(Optional.of(controlsController)) whenever(component.getControlsListingController()).thenReturn(Optional.empty()) + whenever(component.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE) whenever(controlsController.getFavorites()).thenReturn(listOf(mock())) val values = mutableListOf<KeyguardQuickAffordanceConfig.State>() @@ -87,6 +88,7 @@ class HomeControlsKeyguardQuickAffordanceConfigTest : SysuiTestCase() { val controlsController = mock<ControlsController>() whenever(component.getControlsController()).thenReturn(Optional.of(controlsController)) whenever(component.getControlsListingController()).thenReturn(Optional.empty()) + whenever(component.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE) whenever(controlsController.getFavorites()).thenReturn(listOf(mock())) val values = mutableListOf<KeyguardQuickAffordanceConfig.State>() diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt index 171d893640d6..e7b4593b0ebb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt @@ -41,7 +41,6 @@ import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.FakeSystemClock -import com.android.systemui.util.view.ViewUtil import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test @@ -74,8 +73,6 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() { @Mock private lateinit var windowManager: WindowManager @Mock - private lateinit var viewUtil: ViewUtil - @Mock private lateinit var commandQueue: CommandQueue private lateinit var commandQueueCallback: CommandQueue.Callbacks private lateinit var fakeAppIconDrawable: Drawable @@ -102,7 +99,6 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() { context, logger, windowManager, - viewUtil, FakeExecutor(FakeSystemClock()), accessibilityManager, configurationController, @@ -182,7 +178,7 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() { @Test fun setIcon_isAppIcon_usesAppIconSize() { - controllerReceiver.displayChip(getChipReceiverInfo()) + controllerReceiver.displayView(getChipReceiverInfo()) val chipView = getChipView() controllerReceiver.setIcon(chipView, PACKAGE_NAME) @@ -198,7 +194,7 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() { @Test fun setIcon_notAppIcon_usesGenericIconSize() { - controllerReceiver.displayChip(getChipReceiverInfo()) + controllerReceiver.displayView(getChipReceiverInfo()) val chipView = getChipView() controllerReceiver.setIcon(chipView, appPackageName = null) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt index 1061e3c6b0d5..52b6eed9a14d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt @@ -42,7 +42,6 @@ import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.FakeSystemClock -import com.android.systemui.util.view.ViewUtil import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test @@ -51,8 +50,8 @@ import org.mockito.ArgumentCaptor import org.mockito.Mock import org.mockito.Mockito.never import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidTestingRunner::class) @@ -75,8 +74,6 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Mock private lateinit var windowManager: WindowManager @Mock - private lateinit var viewUtil: ViewUtil - @Mock private lateinit var commandQueue: CommandQueue private lateinit var commandQueueCallback: CommandQueue.Callbacks private lateinit var fakeAppIconDrawable: Drawable @@ -110,7 +107,6 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { context, logger, windowManager, - viewUtil, fakeExecutor, accessibilityManager, configurationController, @@ -309,7 +305,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun almostCloseToStartCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() { val state = almostCloseToStartCast() - controllerSender.displayChip(state) + controllerSender.displayView(state) val chipView = getChipView() assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable) @@ -325,7 +321,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun almostCloseToEndCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() { val state = almostCloseToEndCast() - controllerSender.displayChip(state) + controllerSender.displayView(state) val chipView = getChipView() assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable) @@ -341,7 +337,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun transferToReceiverTriggered_appIcon_loadingIcon_noUndo_noFailureIcon() { val state = transferToReceiverTriggered() - controllerSender.displayChip(state) + controllerSender.displayView(state) val chipView = getChipView() assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable) @@ -357,7 +353,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun transferToThisDeviceTriggered_appIcon_loadingIcon_noUndo_noFailureIcon() { val state = transferToThisDeviceTriggered() - controllerSender.displayChip(state) + controllerSender.displayView(state) val chipView = getChipView() assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable) @@ -373,7 +369,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun transferToReceiverSucceeded_appIcon_deviceName_noLoadingIcon_noFailureIcon() { val state = transferToReceiverSucceeded() - controllerSender.displayChip(state) + controllerSender.displayView(state) val chipView = getChipView() assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable) @@ -387,7 +383,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun transferToReceiverSucceeded_nullUndoRunnable_noUndo() { - controllerSender.displayChip(transferToReceiverSucceeded(undoCallback = null)) + controllerSender.displayView(transferToReceiverSucceeded(undoCallback = null)) val chipView = getChipView() assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE) @@ -398,7 +394,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { val undoCallback = object : IUndoMediaTransferCallback.Stub() { override fun onUndoTriggered() {} } - controllerSender.displayChip(transferToReceiverSucceeded(undoCallback)) + controllerSender.displayView(transferToReceiverSucceeded(undoCallback)) val chipView = getChipView() assertThat(chipView.getUndoButton().visibility).isEqualTo(View.VISIBLE) @@ -414,7 +410,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { } } - controllerSender.displayChip(transferToReceiverSucceeded(undoCallback)) + controllerSender.displayView(transferToReceiverSucceeded(undoCallback)) getChipView().getUndoButton().performClick() assertThat(undoCallbackCalled).isTrue() @@ -425,7 +421,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { val undoCallback = object : IUndoMediaTransferCallback.Stub() { override fun onUndoTriggered() {} } - controllerSender.displayChip(transferToReceiverSucceeded(undoCallback)) + controllerSender.displayView(transferToReceiverSucceeded(undoCallback)) getChipView().getUndoButton().performClick() @@ -440,7 +436,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun transferToThisDeviceSucceeded_appIcon_deviceName_noLoadingIcon_noFailureIcon() { val state = transferToThisDeviceSucceeded() - controllerSender.displayChip(state) + controllerSender.displayView(state) val chipView = getChipView() assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable) @@ -454,7 +450,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun transferToThisDeviceSucceeded_nullUndoRunnable_noUndo() { - controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback = null)) + controllerSender.displayView(transferToThisDeviceSucceeded(undoCallback = null)) val chipView = getChipView() assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE) @@ -465,7 +461,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { val undoCallback = object : IUndoMediaTransferCallback.Stub() { override fun onUndoTriggered() {} } - controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback)) + controllerSender.displayView(transferToThisDeviceSucceeded(undoCallback)) val chipView = getChipView() assertThat(chipView.getUndoButton().visibility).isEqualTo(View.VISIBLE) @@ -481,7 +477,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { } } - controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback)) + controllerSender.displayView(transferToThisDeviceSucceeded(undoCallback)) getChipView().getUndoButton().performClick() assertThat(undoCallbackCalled).isTrue() @@ -492,7 +488,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { val undoCallback = object : IUndoMediaTransferCallback.Stub() { override fun onUndoTriggered() {} } - controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback)) + controllerSender.displayView(transferToThisDeviceSucceeded(undoCallback)) getChipView().getUndoButton().performClick() @@ -507,7 +503,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun transferToReceiverFailed_appIcon_noDeviceName_noLoadingIcon_noUndo_failureIcon() { val state = transferToReceiverFailed() - controllerSender.displayChip(state) + controllerSender.displayView(state) val chipView = getChipView() assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable) @@ -523,7 +519,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun transferToThisDeviceFailed_appIcon_noDeviceName_noLoadingIcon_noUndo_failureIcon() { val state = transferToThisDeviceFailed() - controllerSender.displayChip(state) + controllerSender.displayView(state) val chipView = getChipView() assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable) @@ -538,24 +534,24 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun changeFromAlmostCloseToStartToTransferTriggered_loadingIconAppears() { - controllerSender.displayChip(almostCloseToStartCast()) - controllerSender.displayChip(transferToReceiverTriggered()) + controllerSender.displayView(almostCloseToStartCast()) + controllerSender.displayView(transferToReceiverTriggered()) assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.VISIBLE) } @Test fun changeFromTransferTriggeredToTransferSucceeded_loadingIconDisappears() { - controllerSender.displayChip(transferToReceiverTriggered()) - controllerSender.displayChip(transferToReceiverSucceeded()) + controllerSender.displayView(transferToReceiverTriggered()) + controllerSender.displayView(transferToReceiverSucceeded()) assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.GONE) } @Test fun changeFromTransferTriggeredToTransferSucceeded_undoButtonAppears() { - controllerSender.displayChip(transferToReceiverTriggered()) - controllerSender.displayChip( + controllerSender.displayView(transferToReceiverTriggered()) + controllerSender.displayView( transferToReceiverSucceeded( object : IUndoMediaTransferCallback.Stub() { override fun onUndoTriggered() {} @@ -568,26 +564,26 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun changeFromTransferSucceededToAlmostCloseToStart_undoButtonDisappears() { - controllerSender.displayChip(transferToReceiverSucceeded()) - controllerSender.displayChip(almostCloseToStartCast()) + controllerSender.displayView(transferToReceiverSucceeded()) + controllerSender.displayView(almostCloseToStartCast()) assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.GONE) } @Test fun changeFromTransferTriggeredToTransferFailed_failureIconAppears() { - controllerSender.displayChip(transferToReceiverTriggered()) - controllerSender.displayChip(transferToReceiverFailed()) + controllerSender.displayView(transferToReceiverTriggered()) + controllerSender.displayView(transferToReceiverFailed()) assertThat(getChipView().getFailureIcon().visibility).isEqualTo(View.VISIBLE) } @Test - fun transferToReceiverTriggeredThenRemoveChip_chipStillDisplayed() { - controllerSender.displayChip(transferToReceiverTriggered()) + fun transferToReceiverTriggeredThenRemoveView_viewStillDisplayed() { + controllerSender.displayView(transferToReceiverTriggered()) fakeClock.advanceTime(1000L) - controllerSender.removeChip("fakeRemovalReason") + controllerSender.removeView("fakeRemovalReason") fakeExecutor.runAllReady() verify(windowManager, never()).removeView(any()) @@ -596,9 +592,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun transferToReceiverTriggeredThenFarFromReceiver_eventuallyTimesOut() { val state = transferToReceiverTriggered() - controllerSender.displayChip(state) + controllerSender.displayView(state) fakeClock.advanceTime(1000L) - controllerSender.removeChip("fakeRemovalReason") + controllerSender.removeView("fakeRemovalReason") fakeClock.advanceTime(TIMEOUT + 1L) @@ -606,11 +602,11 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { } @Test - fun transferToThisDeviceTriggeredThenRemoveChip_chipStillDisplayed() { - controllerSender.displayChip(transferToThisDeviceTriggered()) + fun transferToThisDeviceTriggeredThenRemoveView_viewStillDisplayed() { + controllerSender.displayView(transferToThisDeviceTriggered()) fakeClock.advanceTime(1000L) - controllerSender.removeChip("fakeRemovalReason") + controllerSender.removeView("fakeRemovalReason") fakeExecutor.runAllReady() verify(windowManager, never()).removeView(any()) @@ -619,9 +615,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun transferToThisDeviceTriggeredThenFarFromReceiver_eventuallyTimesOut() { val state = transferToThisDeviceTriggered() - controllerSender.displayChip(state) + controllerSender.displayView(state) fakeClock.advanceTime(1000L) - controllerSender.removeChip("fakeRemovalReason") + controllerSender.removeView("fakeRemovalReason") fakeClock.advanceTime(TIMEOUT + 1L) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java index f2670131081f..3224a6f970be 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java @@ -547,8 +547,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { mNavigationModeController, mFragmentService, mContentResolver, - mQuickAccessWalletController, - mQrCodeScannerController, mRecordingController, mLargeScreenShadeHeaderController, mScreenOffAnimationController, @@ -556,7 +554,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { mPanelExpansionStateManager, mNotificationRemoteInputManager, mSysUIUnfoldComponent, - mControlsComponent, mInteractionJankMonitor, mQsFrameTranslateController, mSysUiState, @@ -569,8 +566,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { mShadeTransitionController, mSystemClock, mock(CameraGestureHelper.class), - () -> mKeyguardBottomAreaViewModel, - () -> mKeyguardBottomAreaInteractor); + mKeyguardBottomAreaViewModel, + mKeyguardBottomAreaInteractor); mNotificationPanelViewController.initDependencies( mCentralSurfaces, () -> {}, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt index d2970a63a860..97c0bb2c09ca 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt @@ -27,6 +27,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.dock.DockManager import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.FalsingManager +import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.tuner.TunerService import com.android.systemui.tuner.TunerService.Tunable @@ -63,6 +64,8 @@ class PulsingGestureListenerTest : SysuiTestCase() { private lateinit var tunerService: TunerService @Mock private lateinit var dumpManager: DumpManager + @Mock + private lateinit var statusBarStateController: StatusBarStateController private lateinit var tunableCaptor: ArgumentCaptor<Tunable> private lateinit var underTest: PulsingGestureListener @@ -77,6 +80,7 @@ class PulsingGestureListenerTest : SysuiTestCase() { dockManager, centralSurfaces, ambientDisplayConfiguration, + statusBarStateController, tunerService, dumpManager ) @@ -85,6 +89,8 @@ class PulsingGestureListenerTest : SysuiTestCase() { @Test fun testGestureDetector_singleTapEnabled() { + whenever(statusBarStateController.isPulsing).thenReturn(true) + // GIVEN tap is enabled, prox not covered whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true) updateSettings() @@ -102,6 +108,8 @@ class PulsingGestureListenerTest : SysuiTestCase() { @Test fun testGestureDetector_doubleTapEnabled() { + whenever(statusBarStateController.isPulsing).thenReturn(true) + // GIVEN double tap is enabled, prox not covered whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true) updateSettings() @@ -119,6 +127,8 @@ class PulsingGestureListenerTest : SysuiTestCase() { @Test fun testGestureDetector_singleTapEnabled_falsing() { + whenever(statusBarStateController.isPulsing).thenReturn(true) + // GIVEN tap is enabled, prox not covered whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true) updateSettings() @@ -135,7 +145,23 @@ class PulsingGestureListenerTest : SysuiTestCase() { } @Test + fun testGestureDetector_notPulsing_noFalsingCheck() { + whenever(statusBarStateController.isPulsing).thenReturn(false) + + // GIVEN tap is enabled, prox not covered + whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true) + // WHEN there's a tap + underTest.onSingleTapConfirmed(downEv) + + // THEN the falsing manager never gets a call (because the device wasn't pulsing + // during the tap) + verify(falsingManager, never()).isFalseTap(anyInt()) + } + + @Test fun testGestureDetector_doubleTapEnabled_falsing() { + whenever(statusBarStateController.isPulsing).thenReturn(true) + // GIVEN double tap is enabled, prox not covered whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true) updateSettings() @@ -153,6 +179,8 @@ class PulsingGestureListenerTest : SysuiTestCase() { @Test fun testGestureDetector_singleTapEnabled_proxCovered() { + whenever(statusBarStateController.isPulsing).thenReturn(true) + // GIVEN tap is enabled, not a false tap based on classifiers whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true) updateSettings() @@ -170,6 +198,8 @@ class PulsingGestureListenerTest : SysuiTestCase() { @Test fun testGestureDetector_doubleTapEnabled_proxCovered() { + whenever(statusBarStateController.isPulsing).thenReturn(true) + // GIVEN double tap is enabled, not a false tap based on classifiers whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true) updateSettings() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index 8e95a8e6efe0..74e274705e55 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -1053,14 +1053,14 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { } @Test - public void onTrustGrantedMessageDoesShowsOnTrustGranted() { + public void onTrustGrantedMessageShowsOnTrustGranted() { createController(); mController.setVisible(true); // GIVEN trust is granted when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true); - // WHEN the showTrustGranted message is called + // WHEN the showTrustGranted method is called final String trustGrantedMsg = "testing trust granted message"; mController.getKeyguardCallback().showTrustGrantedMessage(trustGrantedMsg); @@ -1071,6 +1071,38 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { } @Test + public void onTrustGrantedMessage_nullMessage_showsDefaultMessage() { + createController(); + mController.setVisible(true); + + // GIVEN trust is granted + when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true); + + // WHEN the showTrustGranted method is called with a null message + mController.getKeyguardCallback().showTrustGrantedMessage(null); + + // THEN verify the default trust granted message shows + verifyIndicationMessage( + INDICATION_TYPE_TRUST, + getContext().getString(R.string.keyguard_indication_trust_unlocked)); + } + + @Test + public void onTrustGrantedMessage_emptyString_showsNoMessage() { + createController(); + mController.setVisible(true); + + // GIVEN trust is granted + when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true); + + // WHEN the showTrustGranted method is called with an EMPTY string + mController.getKeyguardCallback().showTrustGrantedMessage(""); + + // THEN verify NO trust message is shown + verifyNoMessage(INDICATION_TYPE_TRUST); + } + + @Test public void coEx_faceSuccess_showsPressToOpen() { // GIVEN bouncer isn't showing, can skip bouncer, udfps is supported, no a11y enabled when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt deleted file mode 100644 index 3440fa5ac9b1..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt +++ /dev/null @@ -1,54 +0,0 @@ -package com.android.systemui.statusbar.phone - -import android.testing.AndroidTestingRunner -import android.testing.TestableLooper -import android.view.LayoutInflater -import androidx.test.filters.SmallTest -import com.android.systemui.R -import com.android.systemui.SysuiTestCase -import com.android.systemui.assist.AssistManager -import com.android.systemui.plugins.ActivityStarter -import com.android.systemui.statusbar.policy.AccessibilityController -import com.android.systemui.statusbar.policy.FlashlightController -import com.android.systemui.statusbar.policy.KeyguardStateController -import com.android.systemui.tuner.TunerService -import java.util.concurrent.Executor -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.MockitoAnnotations - -@SmallTest -@RunWith(AndroidTestingRunner::class) -@TestableLooper.RunWithLooper(setAsMainLooper = true) -class KeyguardBottomAreaTest : SysuiTestCase() { - - @Mock - private lateinit var mCentralSurfaces: CentralSurfaces - private lateinit var mKeyguardBottomArea: KeyguardBottomAreaView - - @Before - fun setup() { - MockitoAnnotations.initMocks(this) - // Mocked dependencies - mDependency.injectMockDependency(AccessibilityController::class.java) - mDependency.injectMockDependency(ActivityStarter::class.java) - mDependency.injectMockDependency(AssistManager::class.java) - mDependency.injectTestDependency(Executor::class.java, Executor { it.run() }) - mDependency.injectMockDependency(FlashlightController::class.java) - mDependency.injectMockDependency(KeyguardStateController::class.java) - mDependency.injectMockDependency(TunerService::class.java) - - mKeyguardBottomArea = LayoutInflater.from(mContext).inflate( - R.layout.keyguard_bottom_area, null, false) as KeyguardBottomAreaView - } - - @Test - fun initFrom_doesntCrash() { - val other = LayoutInflater.from(mContext).inflate(R.layout.keyguard_bottom_area, - null, false) as KeyguardBottomAreaView - - other.initFrom(mKeyguardBottomArea) - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 2dcdcfce56eb..e790d85763d0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -51,6 +51,7 @@ import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.shade.ShadeController; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationShadeWindowController; +import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionChangeEvent; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; @@ -277,6 +278,17 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test + public void onPanelExpansionChanged_neverTranslatesBouncerWhenShadeLocked() { + when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED); + mStatusBarKeyguardViewManager.onPanelExpansionChanged( + expansionEvent( + /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE, + /* expanded= */ true, + /* tracking= */ false)); + verify(mBouncer, never()).setExpansion(anyFloat()); + } + + @Test public void setOccluded_animatesPanelExpansion_onlyIfBouncerHidden() { mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */); verify(mCentralSurfaces).animateKeyguardUnoccluding(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java index 4a8170fc2955..8f363efd9f51 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java @@ -31,6 +31,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.logging.KeyguardUpdateMonitorLogger; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; @@ -57,6 +58,8 @@ public class KeyguardStateControllerTest extends SysuiTestCase { private DumpManager mDumpManager; @Mock private Lazy<KeyguardUnlockAnimationController> mKeyguardUnlockAnimationControllerLazy; + @Mock + private KeyguardUpdateMonitorLogger mLogger; @Before public void setup() { @@ -66,6 +69,7 @@ public class KeyguardStateControllerTest extends SysuiTestCase { mKeyguardUpdateMonitor, mLockPatternUtils, mKeyguardUnlockAnimationControllerLazy, + mLogger, mDumpManager); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt index f1330682b0e1..e616c26377d2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.media.taptotransfer.common +package com.android.systemui.temporarydisplay import android.content.Context import android.content.pm.ApplicationInfo @@ -30,6 +30,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.media.taptotransfer.common.MediaTttLogger import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener import com.android.systemui.util.concurrency.DelayableExecutor @@ -38,7 +39,6 @@ import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.capture import com.android.systemui.util.time.FakeSystemClock -import com.android.systemui.util.view.ViewUtil import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test @@ -52,8 +52,8 @@ import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @SmallTest -class MediaTttChipControllerCommonTest : SysuiTestCase() { - private lateinit var controllerCommon: TestControllerCommon +class TemporaryViewDisplayControllerTest : SysuiTestCase() { + private lateinit var underTest: TestController private lateinit var fakeClock: FakeSystemClock private lateinit var fakeExecutor: FakeExecutor @@ -72,8 +72,6 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { @Mock private lateinit var windowManager: WindowManager @Mock - private lateinit var viewUtil: ViewUtil - @Mock private lateinit var powerManager: PowerManager @Before @@ -97,11 +95,10 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { fakeClock = FakeSystemClock() fakeExecutor = FakeExecutor(fakeClock) - controllerCommon = TestControllerCommon( + underTest = TestController( context, logger, windowManager, - viewUtil, fakeExecutor, accessibilityManager, configurationController, @@ -110,43 +107,43 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { } @Test - fun displayChip_chipAdded() { - controllerCommon.displayChip(getState()) + fun displayView_viewAdded() { + underTest.displayView(getState()) verify(windowManager).addView(any(), any()) } @Test - fun displayChip_screenOff_screenWakes() { + fun displayView_screenOff_screenWakes() { whenever(powerManager.isScreenOn).thenReturn(false) - controllerCommon.displayChip(getState()) + underTest.displayView(getState()) verify(powerManager).wakeUp(any(), any(), any()) } @Test - fun displayChip_screenAlreadyOn_screenNotWoken() { + fun displayView_screenAlreadyOn_screenNotWoken() { whenever(powerManager.isScreenOn).thenReturn(true) - controllerCommon.displayChip(getState()) + underTest.displayView(getState()) verify(powerManager, never()).wakeUp(any(), any(), any()) } @Test - fun displayChip_twice_chipNotAddedTwice() { - controllerCommon.displayChip(getState()) + fun displayView_twice_viewNotAddedTwice() { + underTest.displayView(getState()) reset(windowManager) - controllerCommon.displayChip(getState()) + underTest.displayView(getState()) verify(windowManager, never()).addView(any(), any()) } @Test - fun displayChip_chipDoesNotDisappearsBeforeTimeout() { + fun displayView_viewDoesNotDisappearsBeforeTimeout() { val state = getState() - controllerCommon.displayChip(state) + underTest.displayView(state) reset(windowManager) fakeClock.advanceTime(TIMEOUT_MS - 1) @@ -155,9 +152,9 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { } @Test - fun displayChip_chipDisappearsAfterTimeout() { + fun displayView_viewDisappearsAfterTimeout() { val state = getState() - controllerCommon.displayChip(state) + underTest.displayView(state) reset(windowManager) fakeClock.advanceTime(TIMEOUT_MS + 1) @@ -166,176 +163,176 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { } @Test - fun displayChip_calledAgainBeforeTimeout_timeoutReset() { - // First, display the chip + fun displayView_calledAgainBeforeTimeout_timeoutReset() { + // First, display the view val state = getState() - controllerCommon.displayChip(state) + underTest.displayView(state) - // After some time, re-display the chip + // After some time, re-display the view val waitTime = 1000L fakeClock.advanceTime(waitTime) - controllerCommon.displayChip(getState()) + underTest.displayView(getState()) // Wait until the timeout for the first display would've happened fakeClock.advanceTime(TIMEOUT_MS - waitTime + 1) - // Verify we didn't hide the chip + // Verify we didn't hide the view verify(windowManager, never()).removeView(any()) } @Test - fun displayChip_calledAgainBeforeTimeout_eventuallyTimesOut() { - // First, display the chip + fun displayView_calledAgainBeforeTimeout_eventuallyTimesOut() { + // First, display the view val state = getState() - controllerCommon.displayChip(state) + underTest.displayView(state) - // After some time, re-display the chip + // After some time, re-display the view fakeClock.advanceTime(1000L) - controllerCommon.displayChip(getState()) + underTest.displayView(getState()) - // Ensure we still hide the chip eventually + // Ensure we still hide the view eventually fakeClock.advanceTime(TIMEOUT_MS + 1) verify(windowManager).removeView(any()) } @Test - fun displayScaleChange_chipReinflatedWithMostRecentState() { - controllerCommon.displayChip(getState(name = "First name")) - controllerCommon.displayChip(getState(name = "Second name")) + fun displayScaleChange_viewReinflatedWithMostRecentState() { + underTest.displayView(getState(name = "First name")) + underTest.displayView(getState(name = "Second name")) reset(windowManager) getConfigurationListener().onDensityOrFontScaleChanged() verify(windowManager).removeView(any()) verify(windowManager).addView(any(), any()) - assertThat(controllerCommon.mostRecentChipInfo?.name).isEqualTo("Second name") + assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("Second name") } @Test - fun removeChip_chipRemovedAndRemovalLogged() { - // First, add the chip - controllerCommon.displayChip(getState()) + fun removeView_viewRemovedAndRemovalLogged() { + // First, add the view + underTest.displayView(getState()) // Then, remove it val reason = "test reason" - controllerCommon.removeChip(reason) + underTest.removeView(reason) verify(windowManager).removeView(any()) verify(logger).logChipRemoval(reason) } @Test - fun removeChip_noAdd_viewNotRemoved() { - controllerCommon.removeChip("reason") + fun removeView_noAdd_viewNotRemoved() { + underTest.removeView("reason") verify(windowManager, never()).removeView(any()) } @Test fun setIcon_nullAppIconDrawableAndNullPackageName_stillHasIcon() { - controllerCommon.displayChip(getState()) - val chipView = getChipView() + underTest.displayView(getState()) + val view = getView() - controllerCommon.setIcon(chipView, appPackageName = null, appIconDrawableOverride = null) + underTest.setIcon(view, appPackageName = null, appIconDrawableOverride = null) - assertThat(chipView.getAppIconView().drawable).isNotNull() + assertThat(view.getAppIconView().drawable).isNotNull() } @Test fun setIcon_nullAppIconDrawableAndInvalidPackageName_stillHasIcon() { - controllerCommon.displayChip(getState()) - val chipView = getChipView() + underTest.displayView(getState()) + val view = getView() - controllerCommon.setIcon( - chipView, appPackageName = "fakePackageName", appIconDrawableOverride = null + underTest.setIcon( + view, appPackageName = "fakePackageName", appIconDrawableOverride = null ) - assertThat(chipView.getAppIconView().drawable).isNotNull() + assertThat(view.getAppIconView().drawable).isNotNull() } @Test fun setIcon_nullAppIconDrawable_iconIsFromPackageName() { - controllerCommon.displayChip(getState()) - val chipView = getChipView() + underTest.displayView(getState()) + val view = getView() - controllerCommon.setIcon(chipView, PACKAGE_NAME, appIconDrawableOverride = null, null) + underTest.setIcon(view, PACKAGE_NAME, appIconDrawableOverride = null, null) - assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconFromPackageName) + assertThat(view.getAppIconView().drawable).isEqualTo(appIconFromPackageName) } @Test fun setIcon_hasAppIconDrawable_iconIsDrawable() { - controllerCommon.displayChip(getState()) - val chipView = getChipView() + underTest.displayView(getState()) + val view = getView() val drawable = context.getDrawable(R.drawable.ic_alarm)!! - controllerCommon.setIcon(chipView, PACKAGE_NAME, drawable, null) + underTest.setIcon(view, PACKAGE_NAME, drawable, null) - assertThat(chipView.getAppIconView().drawable).isEqualTo(drawable) + assertThat(view.getAppIconView().drawable).isEqualTo(drawable) } @Test fun setIcon_nullAppNameAndNullPackageName_stillHasContentDescription() { - controllerCommon.displayChip(getState()) - val chipView = getChipView() + underTest.displayView(getState()) + val view = getView() - controllerCommon.setIcon(chipView, appPackageName = null, appNameOverride = null) + underTest.setIcon(view, appPackageName = null, appNameOverride = null) - assertThat(chipView.getAppIconView().contentDescription.toString()).isNotEmpty() + assertThat(view.getAppIconView().contentDescription.toString()).isNotEmpty() } @Test fun setIcon_nullAppNameAndInvalidPackageName_stillHasContentDescription() { - controllerCommon.displayChip(getState()) - val chipView = getChipView() + underTest.displayView(getState()) + val view = getView() - controllerCommon.setIcon( - chipView, appPackageName = "fakePackageName", appNameOverride = null + underTest.setIcon( + view, appPackageName = "fakePackageName", appNameOverride = null ) - assertThat(chipView.getAppIconView().contentDescription.toString()).isNotEmpty() + assertThat(view.getAppIconView().contentDescription.toString()).isNotEmpty() } @Test fun setIcon_nullAppName_iconContentDescriptionIsFromPackageName() { - controllerCommon.displayChip(getState()) - val chipView = getChipView() + underTest.displayView(getState()) + val view = getView() - controllerCommon.setIcon(chipView, PACKAGE_NAME, null, appNameOverride = null) + underTest.setIcon(view, PACKAGE_NAME, null, appNameOverride = null) - assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME) + assertThat(view.getAppIconView().contentDescription).isEqualTo(APP_NAME) } @Test fun setIcon_hasAppName_iconContentDescriptionIsAppNameOverride() { - controllerCommon.displayChip(getState()) - val chipView = getChipView() + underTest.displayView(getState()) + val view = getView() val appName = "Override App Name" - controllerCommon.setIcon(chipView, PACKAGE_NAME, null, appName) + underTest.setIcon(view, PACKAGE_NAME, null, appName) - assertThat(chipView.getAppIconView().contentDescription).isEqualTo(appName) + assertThat(view.getAppIconView().contentDescription).isEqualTo(appName) } @Test fun setIcon_iconSizeMatchesGetIconSize() { - controllerCommon.displayChip(getState()) - val chipView = getChipView() + underTest.displayView(getState()) + val view = getView() - controllerCommon.setIcon(chipView, PACKAGE_NAME) - chipView.measure( + underTest.setIcon(view, PACKAGE_NAME) + view.measure( View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) ) - assertThat(chipView.getAppIconView().measuredWidth).isEqualTo(ICON_SIZE) - assertThat(chipView.getAppIconView().measuredHeight).isEqualTo(ICON_SIZE) + assertThat(view.getAppIconView().measuredWidth).isEqualTo(ICON_SIZE) + assertThat(view.getAppIconView().measuredHeight).isEqualTo(ICON_SIZE) } - private fun getState(name: String = "name") = ChipInfo(name) + private fun getState(name: String = "name") = ViewInfo(name) - private fun getChipView(): ViewGroup { + private fun getView(): ViewGroup { val viewCaptor = ArgumentCaptor.forClass(View::class.java) verify(windowManager).addView(viewCaptor.capture(), any()) return viewCaptor.value as ViewGroup @@ -349,37 +346,35 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { return callbackCaptor.value } - inner class TestControllerCommon( + inner class TestController( context: Context, logger: MediaTttLogger, windowManager: WindowManager, - viewUtil: ViewUtil, @Main mainExecutor: DelayableExecutor, accessibilityManager: AccessibilityManager, configurationController: ConfigurationController, powerManager: PowerManager, - ) : MediaTttChipControllerCommon<ChipInfo>( + ) : TemporaryViewDisplayController<ViewInfo>( context, logger, windowManager, - viewUtil, mainExecutor, accessibilityManager, configurationController, powerManager, R.layout.media_ttt_chip, ) { - var mostRecentChipInfo: ChipInfo? = null + var mostRecentViewInfo: ViewInfo? = null override val windowLayoutParams = commonWindowLayoutParams - override fun updateChipView(newChipInfo: ChipInfo, currentChipView: ViewGroup) { - super.updateChipView(newChipInfo, currentChipView) - mostRecentChipInfo = newChipInfo + override fun updateView(newInfo: ViewInfo, currentView: ViewGroup) { + super.updateView(newInfo, currentView) + mostRecentViewInfo = newInfo } override fun getIconSize(isAppIcon: Boolean): Int = ICON_SIZE } - inner class ChipInfo(val name: String) : ChipInfoCommon { + inner class ViewInfo(val name: String) : TemporaryViewInfo { override fun getTimeoutMs() = 1L } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt index 11eb4e3de354..42b434a9deaf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt @@ -12,6 +12,7 @@ * 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.keyguard.data.repository diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/FakePowerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt index 15465f4d40fe..15465f4d40fe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/FakePowerRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/FakeUserRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt index 20f1e367944f..20f1e367944f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/FakeUserRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java index c375c73124da..a1adc6f4aff3 100644 --- a/services/core/java/com/android/server/IntentResolver.java +++ b/services/core/java/com/android/server/IntentResolver.java @@ -35,7 +35,6 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.util.FastPrintWriter; import com.android.server.pm.Computer; -import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.snapshot.PackageDataSnapshot; import java.io.PrintWriter; @@ -566,7 +565,7 @@ public abstract class IntentResolver<F, R extends Object> { * "stopped", that is whether it should not be included in the result * if the intent requests to excluded stopped objects. */ - protected boolean isFilterStopped(PackageStateInternal packageState, @UserIdInt int userId) { + protected boolean isFilterStopped(@NonNull Computer computer, F filter, @UserIdInt int userId) { return false; } @@ -805,8 +804,7 @@ public abstract class IntentResolver<F, R extends Object> { int match; if (debug) Slog.v(TAG, "Matching against filter " + filter); - if (excludingStopped && isFilterStopped(computer.getPackageStateInternal(packageName), - userId)) { + if (excludingStopped && isFilterStopped(computer, filter, userId)) { if (debug) { Slog.v(TAG, " Filter's target is stopped; skipping"); } diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 80dd2669a9a9..dbe80c8aee35 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -2534,7 +2534,7 @@ public class OomAdjuster { capability |= capabilityFromFGS; } - capability |= getDefaultCapability(psr, procState); + capability |= getDefaultCapability(app, procState); // Do final modification to adj. Everything we do between here and applying // the final setAdj must be done in this function, because we will also use @@ -2556,7 +2556,7 @@ public class OomAdjuster { || state.getCurCapability() != prevCapability; } - private int getDefaultCapability(ProcessServiceRecord psr, int procState) { + private int getDefaultCapability(ProcessRecord app, int procState) { switch (procState) { case PROCESS_STATE_PERSISTENT: case PROCESS_STATE_PERSISTENT_UI: @@ -2565,15 +2565,13 @@ public class OomAdjuster { case PROCESS_STATE_BOUND_TOP: return PROCESS_CAPABILITY_NETWORK; case PROCESS_STATE_FOREGROUND_SERVICE: - if (psr.hasForegroundServices()) { - // Capability from FGS are conditional depending on foreground service type in - // manifest file and the mAllowWhileInUsePermissionInFgs flag. - return PROCESS_CAPABILITY_NETWORK; + if (app.getActiveInstrumentation() != null) { + return PROCESS_CAPABILITY_ALL_IMPLICIT | PROCESS_CAPABILITY_NETWORK ; } else { - // process has no FGS, the PROCESS_STATE_FOREGROUND_SERVICE is from client. - // the implicit capability could be removed in the future, client should use - // BIND_INCLUDE_CAPABILITY flag. - return PROCESS_CAPABILITY_ALL_IMPLICIT | PROCESS_CAPABILITY_NETWORK; + // Capability from foreground service is conditional depending on + // foregroundServiceType in the manifest file and the + // mAllowWhileInUsePermissionInFgs flag. + return PROCESS_CAPABILITY_NETWORK; } case PROCESS_STATE_BOUND_FOREGROUND_SERVICE: return PROCESS_CAPABILITY_NETWORK; diff --git a/services/core/java/com/android/server/app/TEST_MAPPING b/services/core/java/com/android/server/app/TEST_MAPPING new file mode 100644 index 000000000000..0ba4d8c35523 --- /dev/null +++ b/services/core/java/com/android/server/app/TEST_MAPPING @@ -0,0 +1,23 @@ +{ + "presubmit": [ + { + "name": "CtsGameManagerTestCases", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] + }, + { + "name": "FrameworksMockingServicesTests", + "options": [ + { + "include-filter": "com.android.server.app" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] + } + ] +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java index 4779f6f931ee..6a2731d3982e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java @@ -216,19 +216,31 @@ public abstract class BiometricServiceRegistry<T extends BiometricServiceProvide return null; } - if (mAllProps.size() > 1) { - Slog.e(TAG, "getSingleProvider() called but multiple sensors present: " - + mAllProps.size()); - } + // TODO(b/242837110): remove the try-catch once the bug is fixed. + try { + if (mAllProps.size() > 1) { + Slog.e(TAG, "getSingleProvider() called but multiple sensors present: " + + mAllProps.size()); + } - final int sensorId = mAllProps.get(0).sensorId; - final T provider = getProviderForSensor(sensorId); - if (provider != null) { - return new Pair<>(sensorId, provider); - } + final int sensorId = mAllProps.get(0).sensorId; + final T provider = getProviderForSensor(sensorId); + if (provider != null) { + return new Pair<>(sensorId, provider); + } - Slog.e(TAG, "Single sensor: " + sensorId + ", but provider not found"); - return null; + Slog.e(TAG, "Single sensor: " + sensorId + ", but provider not found"); + return null; + } catch (NullPointerException e) { + final String extra; + if (mAllProps == null) { + extra = "mAllProps: null"; + } else { + extra = "mAllProps.size(): " + mAllProps.size(); + } + Slog.e(TAG, "This shouldn't happen. " + extra, e); + throw e; + } } /** diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 9db9837ffc45..8024d41db90d 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -23,7 +23,6 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_PERMISSION_GR import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE; import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION; import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP; -import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR; import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; @@ -1420,9 +1419,7 @@ final class InstallPackageHelper { } if (!isApex) { - if (!doRenameLI(args, res.mReturnCode, parsedPackage)) { - throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename"); - } + doRenameLI(args, res.mReturnCode, res.mReturnMsg, parsedPackage); try { setUpFsVerityIfPossible(parsedPackage); @@ -1689,19 +1686,20 @@ final class InstallPackageHelper { * scanned package should be updated to reflect the rename. */ @GuardedBy("mPm.mInstallLock") - private boolean doRenameLI(InstallArgs args, int status, ParsedPackage parsedPackage) { + private void doRenameLI(InstallArgs args, int status, String statusMsg, + ParsedPackage parsedPackage) throws PrepareFailure { if (args.mMoveInfo != null) { if (status != PackageManager.INSTALL_SUCCEEDED) { mRemovePackageHelper.cleanUpForMoveInstall(args.mMoveInfo.mToUuid, args.mMoveInfo.mPackageName, args.mMoveInfo.mFromCodePath); - return false; + throw new PrepareFailure(status, statusMsg); } - return true; + return; } // For file installations if (status != PackageManager.INSTALL_SUCCEEDED) { mRemovePackageHelper.removeCodePath(args.mCodeFile); - return false; + throw new PrepareFailure(status, statusMsg); } final File targetDir = resolveTargetDir(args); @@ -1723,12 +1721,14 @@ final class InstallPackageHelper { } } catch (IOException | ErrnoException e) { Slog.w(TAG, "Failed to rename", e); - return false; + throw new PrepareFailure(PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE, + "Failed to rename"); } if (!onIncremental && !SELinux.restoreconRecursive(afterCodeFile)) { Slog.w(TAG, "Failed to restorecon"); - return false; + throw new PrepareFailure(PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE, + "Failed to restorecon"); } // Reflect the rename internally @@ -1739,14 +1739,13 @@ final class InstallPackageHelper { parsedPackage.setPath(afterCodeFile.getCanonicalPath()); } catch (IOException e) { Slog.e(TAG, "Failed to get path: " + afterCodeFile, e); - return false; + throw new PrepareFailure(PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE, + "Failed to get path: " + afterCodeFile); } parsedPackage.setBaseApkPath(FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile, parsedPackage.getBaseApkPath())); parsedPackage.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile, parsedPackage.getSplitCodePaths())); - - return true; } // TODO(b/168126411): Once staged install flow starts using the same folder as non-staged diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java index a1d505453483..62655843a5f2 100644 --- a/services/core/java/com/android/server/pm/UserManagerInternal.java +++ b/services/core/java/com/android/server/pm/UserManagerInternal.java @@ -318,6 +318,10 @@ public abstract class UserManagerInternal { * * <p>On most devices this call will be a no-op, but it will be used on devices that support * multiple users on multiple displays (like automotives with passenger displays). + * + * <p><b>NOTE: </b>this method is meant to be used only by {@code UserController} when a user is + * started and it doesn't validate if the display exists. + * */ public abstract void assignUserToDisplay(@UserIdInt int userId, int displayId); @@ -326,6 +330,9 @@ public abstract class UserManagerInternal { * * <p>On most devices this call will be a no-op, but it will be used on devices that support * multiple users on multiple displays (like automotives with passenger displays). + * + * <p><b>NOTE: </b>this method is meant to be used only by {@code UserController} when a user is + * stopped. */ public abstract void unassignUserFromDisplay(@UserIdInt int userId); diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 4bcda2c0265e..1e30757b8417 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -630,7 +630,7 @@ public class UserManagerService extends IUserManager.Stub { /** * Set on on devices that support background users (key) running on secondary displays (value). */ - // TODO(b/239982558): move such logic to a different class (like UserDisplayAssigner) + // TODO(b/244644281): move such logic to a different class (like UserDisplayAssigner) @Nullable @GuardedBy("mUsersOnSecondaryDisplays") private final SparseIntArray mUsersOnSecondaryDisplays; @@ -710,7 +710,8 @@ public class UserManagerService extends IUserManager.Stub { @VisibleForTesting UserManagerService(Context context) { this(context, /* pm= */ null, /* userDataPreparer= */ null, - /* packagesLock= */ new Object(), context.getCacheDir(), /* users= */ null); + /* packagesLock= */ new Object(), context.getCacheDir(), /* users= */ null, + /* usersOnSecondaryDisplays= */ null); } /** @@ -721,13 +722,13 @@ public class UserManagerService extends IUserManager.Stub { UserManagerService(Context context, PackageManagerService pm, UserDataPreparer userDataPreparer, Object packagesLock) { this(context, pm, userDataPreparer, packagesLock, Environment.getDataDirectory(), - /* users= */ null); + /* users= */ null, /* usersOnSecondaryDisplays= */ null); } @VisibleForTesting UserManagerService(Context context, PackageManagerService pm, UserDataPreparer userDataPreparer, Object packagesLock, File dataDir, - SparseArray<UserData> users) { + SparseArray<UserData> users, @Nullable SparseIntArray usersOnSecondaryDisplays) { mContext = context; mPm = pm; mPackagesLock = packagesLock; @@ -758,7 +759,13 @@ public class UserManagerService extends IUserManager.Stub { mUser0Allocations = DBG_ALLOCATION ? new AtomicInteger() : null; emulateSystemUserModeIfNeeded(); mUsersOnSecondaryDisplaysEnabled = UserManager.isUsersOnSecondaryDisplaysEnabled(); - mUsersOnSecondaryDisplays = mUsersOnSecondaryDisplaysEnabled ? new SparseIntArray() : null; + if (mUsersOnSecondaryDisplaysEnabled) { + mUsersOnSecondaryDisplays = usersOnSecondaryDisplays == null + ? new SparseIntArray() // default behavior + : usersOnSecondaryDisplays; // passed by unit test + } else { + mUsersOnSecondaryDisplays = null; + } } void systemReady() { @@ -1720,6 +1727,11 @@ public class UserManagerService extends IUserManager.Stub { return userId == getCurrentUserId(); } + @VisibleForTesting + boolean isUsersOnSecondaryDisplaysEnabled() { + return mUsersOnSecondaryDisplaysEnabled; + } + @Override public boolean isUserVisible(@UserIdInt int userId) { int callingUserId = UserHandle.getCallingUserId(); @@ -1733,7 +1745,8 @@ public class UserManagerService extends IUserManager.Stub { return isUserVisibleUnchecked(userId); } - private boolean isUserVisibleUnchecked(@UserIdInt int userId) { + @VisibleForTesting + boolean isUserVisibleUnchecked(@UserIdInt int userId) { // First check current foreground user and their profiles (on main display) if (isCurrentUserOrRunningProfileOfCurrentUser(userId)) { return true; @@ -1750,8 +1763,8 @@ public class UserManagerService extends IUserManager.Stub { } } - // TODO(b/239982558): add unit test - private int getDisplayAssignedToUser(@UserIdInt int userId) { + @VisibleForTesting + int getDisplayAssignedToUser(@UserIdInt int userId) { if (isCurrentUserOrRunningProfileOfCurrentUser(userId)) { return Display.DEFAULT_DISPLAY; } @@ -6719,10 +6732,24 @@ public class UserManagerService extends IUserManager.Stub { + "users on multiple displays"); } - Preconditions.checkArgument(userId != UserHandle.USER_SYSTEM, "Cannot start system user" - + " on secondary display (%d)", displayId); - // TODO(b/239982558): call DisplayManagerInternal to check if display is valid instead - Preconditions.checkArgument(displayId > 0, "Invalid display id (%d)", displayId); + Preconditions.checkArgument(userId != UserHandle.USER_SYSTEM, "Cannot assign system " + + "user to secondary display (%d)", displayId); + Preconditions.checkArgument(displayId != Display.INVALID_DISPLAY, + "Cannot assign to INVALID_DISPLAY (%d)", displayId); + + int currentUserId = getCurrentUserId(); + Preconditions.checkArgument(userId != currentUserId, + "Cannot assign current user to other displays"); + + boolean isProfile = isProfileUnchecked(userId); + + Preconditions.checkArgument(userId != currentUserId, + "Cannot assign current user to other displays"); + + Preconditions.checkArgument( + !isProfile || getProfileParentIdUnchecked(userId) != currentUserId, + "Cannot assign profile user %d to display %d when its parent is the current " + + "user (%d)", userId, displayId, currentUserId); synchronized (mUsersOnSecondaryDisplays) { if (DBG_MUMD) { @@ -6730,7 +6757,7 @@ public class UserManagerService extends IUserManager.Stub { userId, displayId); } - if (isProfileUnchecked(userId)) { + if (isProfile) { // Profile can only start in the same display as parent int parentUserId = getProfileParentId(userId); int parentDisplayId = mUsersOnSecondaryDisplays.get(parentUserId); @@ -6749,7 +6776,7 @@ public class UserManagerService extends IUserManager.Stub { // is refactored, it should be atomic. if (mUsersOnSecondaryDisplays.valueAt(i) == displayId) { throw new IllegalStateException("Cannot assign " + userId + " to " - + "display " + displayId + " as it's already assigned to " + + "display " + displayId + " as it's already assigned to " + "user " + mUsersOnSecondaryDisplays.keyAt(i)); } // TODO(b/239982558) also check that user is not already assigned to other diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java index 7baec6217e2e..1d95e8744c47 100644 --- a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java +++ b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java @@ -930,12 +930,14 @@ public class ComponentResolver extends ComponentResolverLocked implements } @Override - protected boolean isFilterStopped(@Nullable PackageStateInternal packageState, + protected boolean isFilterStopped(@NonNull Computer computer, F filter, @UserIdInt int userId) { if (!mUserManager.exists(userId)) { return true; } + final PackageStateInternal packageState = computer.getPackageStateInternal( + filter.first.getPackageName()); if (packageState == null || packageState.getPkg() == null) { return false; } diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index e00e02999b0c..e21feae65c97 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -1211,7 +1211,7 @@ public class TrustManagerService extends SystemService { if (info.userId == userId && info.agent.isTrusted() && info.agent.shouldDisplayTrustGrantedMessage() - && !TextUtils.isEmpty(info.agent.getMessage())) { + && info.agent.getMessage() != null) { trustGrantedMessages.add(info.agent.getMessage().toString()); } } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index f25929c36060..d0b058b4c8f2 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -2713,6 +2713,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub checkPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT); synchronized (mLock) { WallpaperData data = mWallpaperMap.get(mCurrentUserId); + if (data == null) { + data = mWallpaperMap.get(UserHandle.USER_SYSTEM); + if (data == null) { + Slog.e(TAG, "getWallpaperDimAmount: wallpaperData is null"); + return 0.0f; + } + } return data.mWallpaperDimAmount; } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index be801182980d..16b5ee54a5ba 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2120,7 +2120,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mActivityRecordInputSink = new ActivityRecordInputSink(this, sourceRecord); - updateEnterpriseThumbnailDrawable(mAtmService.mUiContext); + updateEnterpriseThumbnailDrawable(mAtmService.getUiContext()); } /** @@ -7424,7 +7424,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } final Rect frame = win.getRelativeFrame(); final Drawable thumbnailDrawable = task.mUserId == mWmService.mCurrentUserId - ? mAtmService.mUiContext.getDrawable(R.drawable.ic_account_circle) + ? mAtmService.getUiContext().getDrawable(R.drawable.ic_account_circle) : mEnterpriseThumbnailDrawable; final HardwareBuffer thumbnail = getDisplayContent().mAppTransition .createCrossProfileAppsThumbnail(thumbnailDrawable, frame); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index bdbe787388be..8f5d838488e2 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -345,7 +345,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { * This Context is themable and meant for UI display (AlertDialogs, etc.). The theme can * change at runtime. Use mContext for non-UI purposes. */ - final Context mUiContext; + private final Context mUiContext; final ActivityThread mSystemThread; H mH; UiHandler mUiHandler; @@ -1040,6 +1040,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + Context getUiContext() { + return mUiContext; + } + UserManagerService getUserManager() { if (mUserManager == null) { IBinder b = ServiceManager.getService(Context.USER_SERVICE); diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index c940a658015d..d2c098b73e71 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -39,6 +39,7 @@ import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE; +import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_OLD_NONE; import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; @@ -746,7 +747,8 @@ public class AppTransition implements Dump { if (isKeyguardGoingAwayTransitOld(transit) && enter) { a = mTransitionAnimation.loadKeyguardExitAnimation(mNextAppTransitionFlags, transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER); - } else if (transit == TRANSIT_OLD_KEYGUARD_OCCLUDE) { + } else if (transit == TRANSIT_OLD_KEYGUARD_OCCLUDE + || transit == TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM) { a = null; } else if (transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE && !enter) { a = mTransitionAnimation.loadKeyguardUnoccludeAnimation(); @@ -1158,6 +1160,9 @@ public class AppTransition implements Dump { case TRANSIT_OLD_KEYGUARD_OCCLUDE: { return "TRANSIT_OLD_KEYGUARD_OCCLUDE"; } + case TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM: { + return "TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM"; + } case TRANSIT_OLD_KEYGUARD_UNOCCLUDE: { return "TRANSIT_OLD_KEYGUARD_UNOCCLUDE"; } @@ -1413,6 +1418,7 @@ public class AppTransition implements Dump { static boolean isKeyguardOccludeTransitOld(@TransitionOldType int transit) { return transit == TRANSIT_OLD_KEYGUARD_OCCLUDE + || transit == TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM || transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE; } diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 4b0005d77e40..7e62c61572d6 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -34,6 +34,7 @@ import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE; +import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_OLD_NONE; import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; @@ -357,8 +358,14 @@ public class AppTransitionController { // When there is a closing app, the keyguard has already been occluded by an // activity, and another activity has started on top of that activity, so normal // app transition animation should be used. - return closingApps.isEmpty() ? TRANSIT_OLD_KEYGUARD_OCCLUDE - : TRANSIT_OLD_ACTIVITY_OPEN; + if (!closingApps.isEmpty()) { + return TRANSIT_OLD_ACTIVITY_OPEN; + } + if (!openingApps.isEmpty() && openingApps.valueAt(0).getActivityType() + == ACTIVITY_TYPE_DREAM) { + return TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM; + } + return TRANSIT_OLD_KEYGUARD_OCCLUDE; case TRANSIT_KEYGUARD_UNOCCLUDE: return TRANSIT_OLD_KEYGUARD_UNOCCLUDE; } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 537b04d7059c..e7806060c14f 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -421,7 +421,7 @@ public class DisplayPolicy { mService = service; mContext = displayContent.isDefaultDisplay ? service.mContext : service.mContext.createDisplayContext(displayContent.getDisplay()); - mUiContext = displayContent.isDefaultDisplay ? service.mAtmService.mUiContext + mUiContext = displayContent.isDefaultDisplay ? service.mAtmService.getUiContext() : service.mAtmService.mSystemThread .getSystemUiContext(displayContent.getDisplayId()); mDisplayContent = displayContent; diff --git a/services/tests/mockingservicestests/src/android/service/games/GameSessionTrampolineActivityTest.java b/services/tests/mockingservicestests/src/android/service/games/GameSessionTrampolineActivityTest.java index d68b517ca8cd..353341ed2985 100644 --- a/services/tests/mockingservicestests/src/android/service/games/GameSessionTrampolineActivityTest.java +++ b/services/tests/mockingservicestests/src/android/service/games/GameSessionTrampolineActivityTest.java @@ -42,6 +42,7 @@ import android.widget.LinearLayout; import android.widget.TextView; import androidx.test.espresso.NoActivityResumedException; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import com.android.internal.infra.AndroidFuture; @@ -80,6 +81,7 @@ public class GameSessionTrampolineActivityTest { } @Test + @FlakyTest(bugId = 245615459) public void launch_targetActivityFinishesSuccessfully_futureCompletedWithSameResults() { AndroidFuture<GameSessionActivityResult> resultFuture = startTestActivityViaGameSessionTrampolineActivity(); @@ -96,6 +98,7 @@ public class GameSessionTrampolineActivityTest { } @Test + @FlakyTest(bugId = 245616660) public void launch_trampolineActivityProcessDeath_futureCompletedWithSameResults() { setAlwaysFinishActivities(true); diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerInternalTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerInternalTest.java new file mode 100644 index 000000000000..1cff685522a2 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerInternalTest.java @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2022 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.server.pm; + +import static android.os.UserHandle.USER_SYSTEM; +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.INVALID_DISPLAY; + +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.junit.Assert.assertThrows; + +import android.util.Log; + +import org.junit.Test; + +/** + * Run as {@code atest FrameworksMockingServicesTests:com.android.server.pm.UserManagerInternalTest} + */ +public final class UserManagerInternalTest extends UserManagerServiceOrInternalTestCase { + + private static final String TAG = UserManagerInternalTest.class.getSimpleName(); + + // NOTE: most the tests below only apply to MUMD configurations, so we're not adding _mumd_ + // in the test names, but _nonMumd_ instead + + @Test + public void testAssignUserToDisplay_nonMumd_defaultDisplayIgnored() { + mUmi.assignUserToDisplay(USER_ID, DEFAULT_DISPLAY); + + assertNoUserAssignedToDisplay(); + } + + @Test + public void testAssignUserToDisplay_nonMumd_otherDisplay_currentUser() { + mockCurrentUser(USER_ID); + + assertThrows(UnsupportedOperationException.class, + () -> mUmi.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID)); + } + + @Test + public void testAssignUserToDisplay_nonMumd_otherDisplay_startProfileOfcurrentUser() { + mockCurrentUser(PARENT_USER_ID); + addDefaultProfileAndParent(); + startDefaultProfile(); + + assertThrows(UnsupportedOperationException.class, + () -> mUmi.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID)); + } + + @Test + public void testAssignUserToDisplay_nonMumd_otherDisplay_stoppedProfileOfcurrentUser() { + mockCurrentUser(PARENT_USER_ID); + addDefaultProfileAndParent(); + stopDefaultProfile(); + + assertThrows(UnsupportedOperationException.class, + () -> mUmi.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID)); + } + + @Test + public void testAssignUserToDisplay_defaultDisplayIgnored() { + enableUsersOnSecondaryDisplays(); + + mUmi.assignUserToDisplay(USER_ID, DEFAULT_DISPLAY); + + assertNoUserAssignedToDisplay(); + } + + @Test + public void testAssignUserToDisplay_systemUser() { + enableUsersOnSecondaryDisplays(); + + assertThrows(IllegalArgumentException.class, + () -> mUmi.assignUserToDisplay(USER_SYSTEM, SECONDARY_DISPLAY_ID)); + } + + @Test + public void testAssignUserToDisplay_invalidDisplay() { + enableUsersOnSecondaryDisplays(); + + assertThrows(IllegalArgumentException.class, + () -> mUmi.assignUserToDisplay(USER_ID, INVALID_DISPLAY)); + } + + @Test + public void testAssignUserToDisplay_currentUser() { + enableUsersOnSecondaryDisplays(); + mockCurrentUser(USER_ID); + + assertThrows(IllegalArgumentException.class, + () -> mUmi.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID)); + + assertNoUserAssignedToDisplay(); + } + + @Test + public void testAssignUserToDisplay_startedProfileOfCurrentUser() { + enableUsersOnSecondaryDisplays(); + mockCurrentUser(PARENT_USER_ID); + addDefaultProfileAndParent(); + startDefaultProfile(); + + IllegalArgumentException e = assertThrows(IllegalArgumentException.class, + () -> mUmi.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID)); + + Log.v(TAG, "Exception: " + e); + assertWithMessage("exception (%s) message", e).that(e).hasMessageThat() + .matches("Cannot.*" + PROFILE_USER_ID + ".*" + SECONDARY_DISPLAY_ID + + ".*parent.*current.*" + PARENT_USER_ID + ".*"); + assertNoUserAssignedToDisplay(); + } + + @Test + public void testAssignUserToDisplay_stoppedProfileOfCurrentUser() { + enableUsersOnSecondaryDisplays(); + mockCurrentUser(PARENT_USER_ID); + addDefaultProfileAndParent(); + stopDefaultProfile(); + + IllegalArgumentException e = assertThrows(IllegalArgumentException.class, + () -> mUmi.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID)); + + Log.v(TAG, "Exception: " + e); + assertWithMessage("exception (%s) message", e).that(e).hasMessageThat() + .matches("Cannot.*" + PROFILE_USER_ID + ".*" + SECONDARY_DISPLAY_ID + + ".*parent.*current.*" + PARENT_USER_ID + ".*"); + + assertNoUserAssignedToDisplay(); + } + + @Test + public void testAssignUserToDisplay_displayAvailable() { + enableUsersOnSecondaryDisplays(); + + mUmi.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID); + + assertUserAssignedToDisplay(USER_ID, SECONDARY_DISPLAY_ID); + } + + @Test + public void testAssignUserToDisplay_displayAlreadyAssigned() { + enableUsersOnSecondaryDisplays(); + + mUmi.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID); + + IllegalStateException e = assertThrows(IllegalStateException.class, + () -> mUmi.assignUserToDisplay(OTHER_USER_ID, SECONDARY_DISPLAY_ID)); + + Log.v(TAG, "Exception: " + e); + assertWithMessage("exception (%s) message", e).that(e).hasMessageThat() + .matches("Cannot.*" + OTHER_USER_ID + ".*" + SECONDARY_DISPLAY_ID + ".*already.*" + + USER_ID + ".*"); + } + + @Test + public void testAssignUserToDisplay_profileOnSameDisplayAsParent() { + enableUsersOnSecondaryDisplays(); + addDefaultProfileAndParent(); + + mUmi.assignUserToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID); + mUmi.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID); + + assertUsersAssignedToDisplays(PARENT_USER_ID, SECONDARY_DISPLAY_ID, + pair(PROFILE_USER_ID, SECONDARY_DISPLAY_ID)); + } + + @Test + public void testAssignUserToDisplay_profileOnDifferentDisplayAsParent() { + enableUsersOnSecondaryDisplays(); + addDefaultProfileAndParent(); + + mUmi.assignUserToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID); + IllegalStateException e = assertThrows(IllegalStateException.class, + () -> mUmi.assignUserToDisplay(PROFILE_USER_ID, OTHER_SECONDARY_DISPLAY_ID)); + + Log.v(TAG, "Exception: " + e); + assertWithMessage("exception (%s) message", e).that(e).hasMessageThat() + .matches("Cannot.*" + PROFILE_USER_ID + ".*" + OTHER_SECONDARY_DISPLAY_ID + + ".*parent.*" + PARENT_USER_ID + ".*" + SECONDARY_DISPLAY_ID + ".*"); + assertUserAssignedToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID); + } + + @Test + public void testUnassignUserFromDisplay_nonMumd_ignored() { + mockCurrentUser(USER_ID); + + mUmi.unassignUserFromDisplay(USER_SYSTEM); + mUmi.unassignUserFromDisplay(USER_ID); + mUmi.unassignUserFromDisplay(OTHER_USER_ID); + + assertNoUserAssignedToDisplay(); + } + + @Test + public void testUnassignUserFromDisplay() { + testAssignUserToDisplay_displayAvailable(); + + mUmi.unassignUserFromDisplay(USER_ID); + + assertNoUserAssignedToDisplay(); + } + + @Override + protected boolean isUserVisible(int userId) { + return mUmi.isUserVisible(userId); + } + + @Override + protected boolean isUserVisibleOnDisplay(int userId, int displayId) { + return mUmi.isUserVisible(userId, displayId); + } + + @Override + protected int getDisplayAssignedToUser(int userId) { + return mUmi.getDisplayAssignedToUser(userId); + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java new file mode 100644 index 000000000000..4f61b8fff995 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java @@ -0,0 +1,570 @@ +/* + * Copyright (C) 2022 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.server.pm; + +import static android.os.UserHandle.USER_NULL; +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.INVALID_DISPLAY; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; + +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.annotation.UserIdInt; +import android.app.ActivityManagerInternal; +import android.content.Context; +import android.content.pm.UserInfo; +import android.os.UserManager; +import android.util.Log; +import android.util.Pair; +import android.util.SparseArray; +import android.util.SparseIntArray; + +import androidx.test.annotation.UiThreadTest; + +import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder; +import com.android.server.ExtendedMockitoTestCase; +import com.android.server.LocalServices; +import com.android.server.am.UserState; +import com.android.server.pm.UserManagerService.UserData; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Base class for {@link UserManagerInternalTest} and {@link UserManagerInternalTest}. + * + * <p>{@link UserManagerService} and its {@link UserManagerInternal} implementation have a + * "symbiotic relationship - some methods of the former simply call the latter and vice versa. + * + * <p>Ideally, only one of them should have the logic, but since that's not the case, this class + * provices the infra to make it easier to test both (which in turn would make it easier / safer to + * refactor their logic later). + */ +abstract class UserManagerServiceOrInternalTestCase extends ExtendedMockitoTestCase { + + private static final String TAG = UserManagerServiceOrInternalTestCase.class.getSimpleName(); + + /** + * Id for a simple user (that doesn't have profiles). + */ + protected static final int USER_ID = 600; + + /** + * Id for another simple user. + */ + protected static final int OTHER_USER_ID = 666; + + /** + * Id for a user that has one profile (whose id is {@link #PROFILE_USER_ID}. + * + * <p>You can use {@link #addDefaultProfileAndParent()} to add both of this user to the service. + */ + protected static final int PARENT_USER_ID = 642; + + /** + * Id for a profile whose parent is {@link #PARENTUSER_ID}. + * + * <p>You can use {@link #addDefaultProfileAndParent()} to add both of this user to the service. + */ + protected static final int PROFILE_USER_ID = 643; + + /** + * Id of a secondary display (i.e, not {@link android.view.Display.DEFAULT_DISPLAY}). + */ + protected static final int SECONDARY_DISPLAY_ID = 42; + + /** + * Id of another secondary display (i.e, not {@link android.view.Display.DEFAULT_DISPLAY}). + */ + protected static final int OTHER_SECONDARY_DISPLAY_ID = 108; + + private final Object mPackagesLock = new Object(); + private final Context mRealContext = androidx.test.InstrumentationRegistry.getInstrumentation() + .getTargetContext(); + private final SparseArray<UserData> mUsers = new SparseArray<>(); + + // TODO(b/244644281): manipulating mUsersOnSecondaryDisplays directly leaks implementation + // details into the unit test, but it's fine for now - in the long term, this logic should be + // refactored into a proper UserDisplayAssignment class. + private final SparseIntArray mUsersOnSecondaryDisplays = new SparseIntArray(); + + private Context mSpiedContext; + private UserManagerService mStandardUms; + private UserManagerService mMumdUms; + private UserManagerInternal mStandardUmi; + private UserManagerInternal mMumdUmi; + + private @Mock PackageManagerService mMockPms; + private @Mock UserDataPreparer mMockUserDataPreparer; + private @Mock ActivityManagerInternal mActivityManagerInternal; + + /** + * Reference to the {@link UserManagerService} being tested. + * + * <p>By default, such service doesn't support {@code MUMD} (Multiple Users on Multiple + * Displays), but that can be changed by calling {@link #enableUsersOnSecondaryDisplays()}. + */ + protected UserManagerService mUms; + + /** + * Reference to the {@link UserManagerInternal} being tested. + * + * <p>By default, such service doesn't support {@code MUMD} (Multiple Users on Multiple + * Displays), but that can be changed by calling {@link #enableUsersOnSecondaryDisplays()}. + */ + protected UserManagerInternal mUmi; + + @Override + protected void initializeSession(StaticMockitoSessionBuilder builder) { + builder + .spyStatic(UserManager.class) + .spyStatic(LocalServices.class); + } + + @Before + @UiThreadTest // Needed to initialize main handler + public final void setFixtures() { + mSpiedContext = spy(mRealContext); + + // Called when WatchedUserStates is constructed + doNothing().when(() -> UserManager.invalidateIsUserUnlockedCache()); + + // Need to set both UserManagerService instances here, as they need to be run in the + // UiThread + + // mMumdUms / mMumdUmi + mockIsUsersOnSecondaryDisplaysEnabled(/* usersOnSecondaryDisplaysEnabled= */ true); + mMumdUms = new UserManagerService(mSpiedContext, mMockPms, mMockUserDataPreparer, + mPackagesLock, mRealContext.getDataDir(), mUsers, mUsersOnSecondaryDisplays); + assertWithMessage("UserManagerService.isUsersOnSecondaryDisplaysEnabled()") + .that(mMumdUms.isUsersOnSecondaryDisplaysEnabled()) + .isTrue(); + mMumdUmi = LocalServices.getService(UserManagerInternal.class); + assertWithMessage("LocalServices.getService(UserManagerInternal.class)").that(mMumdUmi) + .isNotNull(); + resetUserManagerInternal(); + + // mStandardUms / mStandardUmi + mockIsUsersOnSecondaryDisplaysEnabled(/* usersOnSecondaryDisplaysEnabled= */ false); + mStandardUms = new UserManagerService(mSpiedContext, mMockPms, mMockUserDataPreparer, + mPackagesLock, mRealContext.getDataDir(), mUsers, mUsersOnSecondaryDisplays); + assertWithMessage("UserManagerService.isUsersOnSecondaryDisplaysEnabled()") + .that(mStandardUms.isUsersOnSecondaryDisplaysEnabled()) + .isFalse(); + mStandardUmi = LocalServices.getService(UserManagerInternal.class); + assertWithMessage("LocalServices.getService(UserManagerInternal.class)").that(mStandardUmi) + .isNotNull(); + setServiceFixtures(/*usersOnSecondaryDisplaysEnabled= */ false); + } + + @After + public final void resetUserManagerInternal() { + // LocalServices follows the "Highlander rule" - There can be only one! + LocalServices.removeServiceForTest(UserManagerInternal.class); + } + + ////////////////////////////////////////////////////////////////////////////////////////////// + // Methods whose UMS implementation calls UMI or vice-versa - they're tested in this class, // + // but the subclass must provide the proper implementation // + ////////////////////////////////////////////////////////////////////////////////////////////// + + protected abstract boolean isUserVisible(int userId); + protected abstract boolean isUserVisibleOnDisplay(int userId, int displayId); + protected abstract int getDisplayAssignedToUser(int userId); + + ///////////////////////////////// + // Tests for the above methods // + ///////////////////////////////// + + @Test + public void testIsUserVisible_invalidUser() { + mockCurrentUser(USER_ID); + + assertWithMessage("isUserVisible(%s)", USER_NULL).that(isUserVisible(USER_NULL)).isFalse(); + } + + @Test + public void testIsUserVisible_currentUser() { + mockCurrentUser(USER_ID); + + assertWithMessage("isUserVisible(%s)", USER_ID).that(isUserVisible(USER_ID)).isTrue(); + } + + @Test + public void testIsUserVisible_nonCurrentUser() { + mockCurrentUser(OTHER_USER_ID); + + assertWithMessage("isUserVisible(%s)", USER_ID).that(isUserVisible(USER_ID)).isFalse(); + } + + @Test + public void testIsUserVisible_startedProfileOfcurrentUser() { + addDefaultProfileAndParent(); + mockCurrentUser(PARENT_USER_ID); + startDefaultProfile(); + setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED); + + assertWithMessage("isUserVisible(%s)", PROFILE_USER_ID).that(isUserVisible(PROFILE_USER_ID)) + .isTrue(); + } + + @Test + public void testIsUserVisible_stoppedProfileOfcurrentUser() { + addDefaultProfileAndParent(); + mockCurrentUser(PARENT_USER_ID); + stopDefaultProfile(); + + assertWithMessage("isUserVisible(%s)", PROFILE_USER_ID).that(isUserVisible(PROFILE_USER_ID)) + .isFalse(); + } + + @Test + public void testIsUserVisible_bgUserOnSecondaryDisplay() { + enableUsersOnSecondaryDisplays(); + mockCurrentUser(OTHER_USER_ID); + assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID); + + assertWithMessage("isUserVisible(%s)", USER_ID).that(isUserVisible(USER_ID)).isTrue(); + } + + // NOTE: we don't need to add tests for profiles (started / stopped profiles of bg user), as + // isUserVisible() for bg users relies only on the user / display assignments + + @Test + public void testIsUserVisibleOnDisplay_invalidUser() { + mockCurrentUser(USER_ID); + + assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_NULL, DEFAULT_DISPLAY) + .that(isUserVisibleOnDisplay(USER_NULL, DEFAULT_DISPLAY)).isFalse(); + } + + @Test + public void testIsUserVisibleOnDisplay_currentUserInvalidDisplay() { + mockCurrentUser(USER_ID); + + assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_ID, INVALID_DISPLAY) + .that(isUserVisibleOnDisplay(USER_ID, INVALID_DISPLAY)).isTrue(); + } + + @Test + public void testIsUserVisibleOnDisplay_currentUserDefaultDisplay() { + mockCurrentUser(USER_ID); + + assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_ID, DEFAULT_DISPLAY) + .that(isUserVisibleOnDisplay(USER_ID, DEFAULT_DISPLAY)).isTrue(); + } + + @Test + public void testIsUserVisibleOnDisplay_currentUserSecondaryDisplay() { + mockCurrentUser(USER_ID); + + assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_ID, SECONDARY_DISPLAY_ID) + .that(isUserVisibleOnDisplay(USER_ID, SECONDARY_DISPLAY_ID)).isTrue(); + } + + @Test + public void testIsUserVisibleOnDisplay_nonCurrentUserDefaultDisplay() { + mockCurrentUser(OTHER_USER_ID); + + assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_ID, DEFAULT_DISPLAY) + .that(isUserVisibleOnDisplay(USER_ID, DEFAULT_DISPLAY)).isFalse(); + } + + @Test + public void testIsUserVisibleOnDisplay_startedProfileOfcurrentUserInvalidDisplay() { + addDefaultProfileAndParent(); + mockCurrentUser(PARENT_USER_ID); + startDefaultProfile(); + setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED); + + assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, INVALID_DISPLAY) + .that(isUserVisibleOnDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY)).isTrue(); + } + + @Test + public void testIsUserVisibleOnDisplay_stoppedProfileOfcurrentUserInvalidDisplay() { + addDefaultProfileAndParent(); + mockCurrentUser(PARENT_USER_ID); + stopDefaultProfile(); + + assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, INVALID_DISPLAY) + .that(isUserVisibleOnDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY)).isFalse(); + } + + @Test + public void testIsUserVisibleOnDisplay_startedProfileOfcurrentUserDefaultDisplay() { + addDefaultProfileAndParent(); + mockCurrentUser(PARENT_USER_ID); + startDefaultProfile(); + setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED); + + assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, DEFAULT_DISPLAY) + .that(isUserVisibleOnDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY)).isTrue(); + } + + @Test + public void testIsUserVisibleOnDisplay_stoppedProfileOfcurrentUserDefaultDisplay() { + addDefaultProfileAndParent(); + mockCurrentUser(PARENT_USER_ID); + stopDefaultProfile(); + + assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, DEFAULT_DISPLAY) + .that(isUserVisibleOnDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY)).isFalse(); + } + + @Test + public void testIsUserVisibleOnDisplay_startedProfileOfcurrentUserSecondaryDisplay() { + addDefaultProfileAndParent(); + mockCurrentUser(PARENT_USER_ID); + startDefaultProfile(); + setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED); + + assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, SECONDARY_DISPLAY_ID) + .that(isUserVisibleOnDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID)).isTrue(); + } + + @Test + public void testIsUserVisibleOnDisplay_stoppedProfileOfcurrentUserSecondaryDisplay() { + addDefaultProfileAndParent(); + mockCurrentUser(PARENT_USER_ID); + stopDefaultProfile(); + + assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, SECONDARY_DISPLAY_ID) + .that(isUserVisibleOnDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID)).isFalse(); + } + + @Test + public void testIsUserVisibleOnDisplay_bgUserOnSecondaryDisplay() { + enableUsersOnSecondaryDisplays(); + mockCurrentUser(OTHER_USER_ID); + assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID); + + assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_ID, SECONDARY_DISPLAY_ID) + .that(isUserVisibleOnDisplay(USER_ID, SECONDARY_DISPLAY_ID)).isTrue(); + } + + // NOTE: we don't need to add tests for profiles (started / stopped profiles of bg user), as + // isUserVisibleOnDisplay() for bg users relies only on the user / display assignments + + @Test + public void testGetDisplayAssignedToUser_invalidUser() { + mockCurrentUser(USER_ID); + + assertWithMessage("getDisplayAssignedToUser(%s)", USER_NULL) + .that(getDisplayAssignedToUser(USER_NULL)).isEqualTo(INVALID_DISPLAY); + } + + @Test + public void testGetDisplayAssignedToUser_currentUser() { + mockCurrentUser(USER_ID); + + assertWithMessage("getDisplayAssignedToUser(%s)", USER_ID) + .that(getDisplayAssignedToUser(USER_ID)).isEqualTo(DEFAULT_DISPLAY); + } + + @Test + public void testGetDisplayAssignedToUser_nonCurrentUser() { + mockCurrentUser(OTHER_USER_ID); + + assertWithMessage("getDisplayAssignedToUser(%s)", USER_ID) + .that(getDisplayAssignedToUser(USER_ID)).isEqualTo(INVALID_DISPLAY); + } + + @Test + public void testGetDisplayAssignedToUser_startedProfileOfcurrentUser() { + addDefaultProfileAndParent(); + mockCurrentUser(PARENT_USER_ID); + startDefaultProfile(); + setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED); + + assertWithMessage("getDisplayAssignedToUser(%s)", PROFILE_USER_ID) + .that(getDisplayAssignedToUser(PROFILE_USER_ID)).isEqualTo(DEFAULT_DISPLAY); + } + + @Test + public void testGetDisplayAssignedToUser_stoppedProfileOfcurrentUser() { + addDefaultProfileAndParent(); + mockCurrentUser(PARENT_USER_ID); + stopDefaultProfile(); + + assertWithMessage("getDisplayAssignedToUser(%s)", PROFILE_USER_ID) + .that(getDisplayAssignedToUser(PROFILE_USER_ID)).isEqualTo(INVALID_DISPLAY); + } + + @Test + public void testGetDisplayAssignedToUser_bgUserOnSecondaryDisplay() { + enableUsersOnSecondaryDisplays(); + mockCurrentUser(OTHER_USER_ID); + assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID); + + assertWithMessage("getDisplayAssignedToUser(%s)", USER_ID) + .that(getDisplayAssignedToUser(USER_ID)).isEqualTo(SECONDARY_DISPLAY_ID); + } + + // NOTE: we don't need to add tests for profiles (started / stopped profiles of bg user), as + // getDisplayAssignedToUser() for bg users relies only on the user / display assignments + + /////////////////////////////////////////// + // Helper methods exposed to sub-classes // + /////////////////////////////////////////// + + /** + * Change test fixtures to use a version that supports {@code MUMD} (Multiple Users on Multiple + * Displays). + */ + protected final void enableUsersOnSecondaryDisplays() { + setServiceFixtures(/* usersOnSecondaryDisplaysEnabled= */ true); + } + + protected final void mockCurrentUser(@UserIdInt int userId) { + mockGetLocalService(ActivityManagerInternal.class, mActivityManagerInternal); + + when(mActivityManagerInternal.getCurrentUserId()).thenReturn(userId); + } + + protected final <T> void mockGetLocalService(Class<T> serviceClass, T service) { + doReturn(service).when(() -> LocalServices.getService(serviceClass)); + } + + protected final void addDefaultProfileAndParent() { + addUser(PARENT_USER_ID); + addProfile(PROFILE_USER_ID, PARENT_USER_ID); + } + + protected final void addProfile(@UserIdInt int profileId, @UserIdInt int parentId) { + TestUserData profileData = new TestUserData(profileId); + profileData.info.flags = UserInfo.FLAG_PROFILE; + profileData.info.profileGroupId = parentId; + + addUserData(profileData); + } + + protected final void addUser(@UserIdInt int userId) { + TestUserData userData = new TestUserData(userId); + + addUserData(userData); + } + + protected final void startDefaultProfile() { + setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED); + } + + protected final void stopDefaultProfile() { + // TODO(b/244798930): should set it to STATE_STOPPING or STATE_SHUTDOWN instead + removeUserState(PROFILE_USER_ID); + } + + // NOTE: should only called by tests that indirectly needs to check user assignments (like + // isUserVisible), not by tests for the user assignment methods per se. + protected final void assignUserToDisplay(@UserIdInt int userId, int displayId) { + mUsersOnSecondaryDisplays.put(userId, displayId); + } + + protected final void assertNoUserAssignedToDisplay() { + assertWithMessage("mUsersOnSecondaryDisplays()").that(usersOnSecondaryDisplaysAsMap()) + .isEmpty(); + } + + protected final void assertUserAssignedToDisplay(@UserIdInt int userId, int displayId) { + assertWithMessage("mUsersOnSecondaryDisplays()").that(usersOnSecondaryDisplaysAsMap()) + .containsExactly(userId, displayId); + } + + @SafeVarargs + protected final void assertUsersAssignedToDisplays(@UserIdInt int userId, int displayId, + @SuppressWarnings("unchecked") Pair<Integer, Integer>... others) { + Object[] otherObjects = new Object[others.length * 2]; + for (int i = 0; i < others.length; i++) { + Pair<Integer, Integer> other = others[i]; + otherObjects[i * 2] = other.first; + otherObjects[i * 2 + 1] = other.second; + + } + assertWithMessage("mUsersOnSecondaryDisplays()").that(usersOnSecondaryDisplaysAsMap()) + .containsExactly(userId, displayId, otherObjects); + } + + protected static Pair<Integer, Integer> pair(@UserIdInt int userId, int secondaryDisplayId) { + return new Pair<>(userId, secondaryDisplayId); + } + + /////////////////// + // Private infra // + /////////////////// + + private void setServiceFixtures(boolean usersOnSecondaryDisplaysEnabled) { + Log.d(TAG, "Setting fixtures for usersOnSecondaryDisplaysEnabled=" + + usersOnSecondaryDisplaysEnabled); + if (usersOnSecondaryDisplaysEnabled) { + mUms = mMumdUms; + mUmi = mMumdUmi; + } else { + mUms = mStandardUms; + mUmi = mStandardUmi; + } + } + + private void mockIsUsersOnSecondaryDisplaysEnabled(boolean enabled) { + Log.d(TAG, "Mocking UserManager.isUsersOnSecondaryDisplaysEnabled() to return " + enabled); + doReturn(enabled).when(() -> UserManager.isUsersOnSecondaryDisplaysEnabled()); + } + + private void addUserData(TestUserData userData) { + Log.d(TAG, "Adding " + userData); + mUsers.put(userData.info.id, userData); + } + + private void setUserState(@UserIdInt int userId, int userState) { + mUmi.setUserState(userId, userState); + } + + private void removeUserState(@UserIdInt int userId) { + mUmi.removeUserState(userId); + } + + private Map<Integer, Integer> usersOnSecondaryDisplaysAsMap() { + int size = mUsersOnSecondaryDisplays.size(); + Map<Integer, Integer> map = new LinkedHashMap<>(size); + for (int i = 0; i < size; i++) { + map.put(mUsersOnSecondaryDisplays.keyAt(i), mUsersOnSecondaryDisplays.valueAt(i)); + } + return map; + } + + private static final class TestUserData extends UserData { + + @SuppressWarnings("deprecation") + TestUserData(@UserIdInt int userId) { + info = new UserInfo(); + info.id = userId; + } + + @Override + public String toString() { + return "TestUserData[" + info.toFullString() + "]"; + } + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java index 64931116bd01..b335a34f6f14 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java @@ -15,87 +15,20 @@ */ package com.android.server.pm; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; - import static com.google.common.truth.Truth.assertWithMessage; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; - -import android.annotation.UserIdInt; import android.app.ActivityManagerInternal; -import android.content.Context; -import android.content.pm.UserInfo; import android.os.UserHandle; -import android.os.UserManager; -import android.util.Log; -import android.util.SparseArray; - -import androidx.test.annotation.UiThreadTest; -import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder; -import com.android.server.ExtendedMockitoTestCase; -import com.android.server.LocalServices; -import com.android.server.am.UserState; -import com.android.server.pm.UserManagerService.UserData; - -import org.junit.After; -import org.junit.Before; import org.junit.Test; -import org.mockito.Mock; /** * Run as {@code atest FrameworksMockingServicesTests:com.android.server.pm.UserManagerServiceTest} */ -public final class UserManagerServiceTest extends ExtendedMockitoTestCase { - - private static final String TAG = UserManagerServiceTest.class.getSimpleName(); - - private final Object mPackagesLock = new Object(); - private final Context mRealContext = androidx.test.InstrumentationRegistry.getInstrumentation() - .getTargetContext(); - private Context mSpiedContext; - - private @Mock PackageManagerService mMockPms; - private @Mock UserDataPreparer mMockUserDataPreparer; - private @Mock ActivityManagerInternal mActivityManagerInternal; - - private final SparseArray<UserData> mUsers = new SparseArray<>(); - private UserManagerService mUms; - private UserManagerInternal mUmi; - - @Override - protected void initializeSession(StaticMockitoSessionBuilder builder) { - builder - .spyStatic(UserManager.class) - .spyStatic(LocalServices.class); - } - - @Before - @UiThreadTest // Needed to initialize main handler - public void setFixtures() { - mSpiedContext = spy(mRealContext); - - // Called when WatchedUserStates is constructed - doNothing().when(() -> UserManager.invalidateIsUserUnlockedCache()); - - mUms = new UserManagerService(mSpiedContext, mMockPms, mMockUserDataPreparer, - mPackagesLock, mRealContext.getDataDir(), mUsers); - mUmi = LocalServices.getService(UserManagerInternal.class); - - assertWithMessage("LocalServices.getService(UserManagerInternal.class)").that(mUmi) - .isNotNull(); - } - - @After - public void resetLocalService() { - // LocalServices follows the "Highlander rule" - There can be only one! - LocalServices.removeServiceForTest(UserManagerInternal.class); - } +public final class UserManagerServiceTest extends UserManagerServiceOrInternalTestCase { @Test - public void testgetCurrentUserId_amInternalNotReady() { + public void testGetCurrentUserId_amInternalNotReady() { mockGetLocalService(ActivityManagerInternal.class, null); assertWithMessage("getCurrentUserId()").that(mUms.getCurrentUserId()) @@ -103,119 +36,70 @@ public final class UserManagerServiceTest extends ExtendedMockitoTestCase { } @Test - public void testgetCurrentUserId() { - mockCurrentUser(42); + public void testGetCurrentUserId() { + mockCurrentUser(USER_ID); assertWithMessage("getCurrentUserId()").that(mUms.getCurrentUserId()) - .isEqualTo(42); + .isEqualTo(USER_ID); } @Test public void testIsCurrentUserOrRunningProfileOfCurrentUser_currentUser() { - int userId = 42; - mockCurrentUser(userId); + mockCurrentUser(USER_ID); - assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", userId) - .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(userId)).isTrue(); + assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", USER_ID) + .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(USER_ID)).isTrue(); } @Test public void testIsCurrentUserOrRunningProfileOfCurrentUser_notCurrentUser() { - int userId = 42; - mockCurrentUser(108); + mockCurrentUser(OTHER_USER_ID); - assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", userId) - .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(userId)).isFalse(); + assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", USER_ID) + .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(USER_ID)).isFalse(); } @Test public void testIsCurrentUserOrRunningProfileOfCurrentUser_startedProfileOfCurrentUser() { - int parentId = 108; - int profileId = 42; - addUser(parentId); - addProfile(profileId, parentId); - mockCurrentUser(parentId); - setUserState(profileId, UserState.STATE_RUNNING_UNLOCKED); - - assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", profileId) - .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(profileId)).isTrue(); + addDefaultProfileAndParent(); + startDefaultProfile(); + mockCurrentUser(PARENT_USER_ID); + + assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", PROFILE_USER_ID) + .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(PROFILE_USER_ID)).isTrue(); } @Test public void testIsCurrentUserOrRunningProfileOfCurrentUser_stoppedProfileOfCurrentUser() { - int parentId = 108; - int profileId = 42; - addUser(parentId); - addProfile(profileId, parentId); - mockCurrentUser(parentId); - // TODO(b/244798930): should set it to STATE_STOPPING or STATE_SHUTDOWN instead - removeUserState(profileId); - - assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", profileId) - .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(profileId)).isFalse(); + addDefaultProfileAndParent(); + stopDefaultProfile(); + mockCurrentUser(PARENT_USER_ID); + + assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", PROFILE_USER_ID) + .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(PROFILE_USER_ID)).isFalse(); } @Test public void testIsCurrentUserOrRunningProfileOfCurrentUser_profileOfNonCurrentUSer() { - int parentId = 108; - int profileId = 42; - int currentUserId = 666; - addUser(parentId); - addProfile(profileId, parentId); - mockCurrentUser(currentUserId); - - assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", profileId) - .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(profileId)).isFalse(); - } - - private void mockCurrentUser(@UserIdInt int userId) { - mockGetLocalService(ActivityManagerInternal.class, mActivityManagerInternal); - - when(mActivityManagerInternal.getCurrentUserId()).thenReturn(userId); - } - - private <T> void mockGetLocalService(Class<T> serviceClass, T service) { - doReturn(service).when(() -> LocalServices.getService(serviceClass)); - } - - private void addProfile(@UserIdInt int profileId, @UserIdInt int parentId) { - TestUserData profileData = new TestUserData(profileId); - profileData.info.flags = UserInfo.FLAG_PROFILE; - profileData.info.profileGroupId = parentId; - - addUserData(profileData); - } - - private void addUser(@UserIdInt int userId) { - TestUserData userData = new TestUserData(userId); + addDefaultProfileAndParent(); + mockCurrentUser(OTHER_USER_ID); - addUserData(userData); + assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", PROFILE_USER_ID) + .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(PROFILE_USER_ID)).isFalse(); } - private void addUserData(TestUserData userData) { - Log.d(TAG, "Adding " + userData); - mUsers.put(userData.info.id, userData); - } - - private void setUserState(@UserIdInt int userId, int userState) { - mUmi.setUserState(userId, userState); + @Override + protected boolean isUserVisible(int userId) { + return mUms.isUserVisibleUnchecked(userId); } - private void removeUserState(@UserIdInt int userId) { - mUmi.removeUserState(userId); + @Override + protected boolean isUserVisibleOnDisplay(int userId, int displayId) { + return mUms.isUserVisibleOnDisplay(userId, displayId); } - private static final class TestUserData extends UserData { - - @SuppressWarnings("unused") - TestUserData(@UserIdInt int userId) { - info = new UserInfo(); - info.id = userId; - } - - @Override - public String toString() { - return "TestUserData[" + info.toFullString() + "]"; - } + @Override + protected int getDisplayAssignedToUser(int userId) { + return mUms.getDisplayAssignedToUser(userId); } } diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java index 047fcd6ed108..d5b923a81e4a 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java +++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.os.BatteryManager; import android.os.BatteryStats; +import android.os.BatteryStats.HistoryItem; import android.os.BatteryStats.MeasuredEnergyDetails; import android.os.Parcel; import android.util.Log; @@ -40,7 +41,9 @@ import com.android.internal.os.Clock; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InOrder; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.io.File; @@ -63,6 +66,8 @@ public class BatteryStatsHistoryTest { private final Clock mClock = new MockClock(); private BatteryStatsHistory mHistory; @Mock + private BatteryStatsHistory.TraceDelegate mTracer; + @Mock private BatteryStatsHistory.HistoryStepDetailsCalculator mStepDetailsCalculator; @Before @@ -79,13 +84,70 @@ public class BatteryStatsHistoryTest { } mHistoryDir.delete(); mHistory = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32, 1024, - mStepDetailsCalculator, mClock); + mStepDetailsCalculator, mClock, mTracer); when(mStepDetailsCalculator.getHistoryStepDetails()) .thenReturn(new BatteryStats.HistoryStepDetails()); } @Test + public void testAtraceBinaryState1() { + mHistory.forceRecordAllHistory(); + + InOrder inOrder = Mockito.inOrder(mTracer); + Mockito.when(mTracer.tracingEnabled()).thenReturn(true); + + mHistory.recordStateStartEvent(mClock.elapsedRealtime(), + mClock.uptimeMillis(), HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG); + mHistory.recordStateStopEvent(mClock.elapsedRealtime(), + mClock.uptimeMillis(), HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG); + mHistory.recordStateStartEvent(mClock.elapsedRealtime(), + mClock.uptimeMillis(), HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG); + + inOrder.verify(mTracer).traceCounter("battery_stats.mobile_radio", 1); + inOrder.verify(mTracer).traceCounter("battery_stats.mobile_radio", 0); + inOrder.verify(mTracer).traceCounter("battery_stats.mobile_radio", 1); + } + + @Test + public void testAtraceBinaryState2() { + mHistory.forceRecordAllHistory(); + + InOrder inOrder = Mockito.inOrder(mTracer); + Mockito.when(mTracer.tracingEnabled()).thenReturn(true); + + mHistory.recordState2StartEvent(mClock.elapsedRealtime(), + mClock.uptimeMillis(), HistoryItem.STATE2_WIFI_ON_FLAG); + mHistory.recordState2StopEvent(mClock.elapsedRealtime(), + mClock.uptimeMillis(), HistoryItem.STATE2_WIFI_ON_FLAG); + mHistory.recordState2StartEvent(mClock.elapsedRealtime(), + mClock.uptimeMillis(), HistoryItem.STATE2_WIFI_ON_FLAG); + + inOrder.verify(mTracer).traceCounter("battery_stats.wifi", 1); + inOrder.verify(mTracer).traceCounter("battery_stats.wifi", 0); + inOrder.verify(mTracer).traceCounter("battery_stats.wifi", 1); + } + + @Test + public void testAtraceNumericalState() { + mHistory.forceRecordAllHistory(); + + InOrder inOrder = Mockito.inOrder(mTracer); + Mockito.when(mTracer.tracingEnabled()).thenReturn(true); + + mHistory.recordDataConnectionTypeChangeEvent(mClock.elapsedRealtime(), + mClock.uptimeMillis(), 1); + mHistory.recordDataConnectionTypeChangeEvent(mClock.elapsedRealtime(), + mClock.uptimeMillis(), 2); + mHistory.recordDataConnectionTypeChangeEvent(mClock.elapsedRealtime(), + mClock.uptimeMillis(), 3); + + inOrder.verify(mTracer).traceCounter("battery_stats.data_conn", 1); + inOrder.verify(mTracer).traceCounter("battery_stats.data_conn", 2); + inOrder.verify(mTracer).traceCounter("battery_stats.data_conn", 3); + } + + @Test public void testConstruct() { createActiveFile(mHistory); verifyFileNumbers(mHistory, Arrays.asList(0)); @@ -131,7 +193,7 @@ public class BatteryStatsHistoryTest { // create a new BatteryStatsHistory object, it will pick up existing history files. BatteryStatsHistory history2 = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32, 1024, - null, mClock); + null, mClock, mTracer); // verify constructor can pick up all files from file system. verifyFileNumbers(history2, fileList); verifyActiveFile(history2, "33.bin"); diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index 939c7de22356..b0ccbd1cf22f 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -436,15 +436,6 @@ </intent-filter> </activity> - <activity android:name="SurfaceViewAlphaActivity" - android:label="SurfaceView/SurfaceView with Alpha" - android:exported="true"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="com.android.test.hwui.TEST"/> - </intent-filter> - </activity> - <activity android:name=".PenStylusActivity" android:label="Pen/Draw" android:exported="true"> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/SurfaceViewAlphaActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/SurfaceViewAlphaActivity.java deleted file mode 100644 index 01fe6ae0518b..000000000000 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/SurfaceViewAlphaActivity.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2022 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.test.hwui; - -import android.app.Activity; -import android.graphics.Canvas; -import android.graphics.Color; -import android.os.Bundle; -import android.view.SurfaceHolder; -import android.view.SurfaceHolder.Callback; -import android.view.SurfaceView; -import android.view.View; -import android.widget.Button; -import android.widget.LinearLayout; -import android.widget.RelativeLayout; -import android.widget.SeekBar; -import android.widget.TextView; - -public class SurfaceViewAlphaActivity extends Activity implements Callback { - SurfaceView mSurfaceView; - - private enum ZOrder { - ABOVE, - BELOW - } - - private float mAlpha = 127f / 255f; - private ZOrder mZOrder = ZOrder.BELOW; - - - private String getAlphaText() { - return "Alpha: " + mAlpha; - } - - private void toggleZOrder() { - if (ZOrder.ABOVE.equals(mZOrder)) { - mZOrder = ZOrder.BELOW; - } else { - mZOrder = ZOrder.ABOVE; - } - } - - // Overlaps a blue view on the left, then the SurfaceView in the center, then a blue view on the - // right. - private void overlapViews(SurfaceView view, LinearLayout parent) { - float density = getResources().getDisplayMetrics().density; - int surfaceViewSize = (int) (200 * density); - int blueViewSize = (int) (surfaceViewSize * 2 / 3f); - int totalSize = (int) (surfaceViewSize * 5 / 3f); - - RelativeLayout overlapLayout = new RelativeLayout(this); - - RelativeLayout.LayoutParams leftViewLayoutParams = new RelativeLayout.LayoutParams( - blueViewSize, surfaceViewSize); - leftViewLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT); - - View leftBlueView = new View(this); - leftBlueView.setBackgroundColor(Color.BLUE); - overlapLayout.addView(leftBlueView, leftViewLayoutParams); - - RelativeLayout.LayoutParams sVLayoutParams = new RelativeLayout.LayoutParams( - surfaceViewSize, surfaceViewSize); - sVLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT); - overlapLayout.addView(view, sVLayoutParams); - - RelativeLayout.LayoutParams rightViewLayoutParams = new RelativeLayout.LayoutParams( - blueViewSize, surfaceViewSize); - rightViewLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); - - View rightBlueView = new View(this); - rightBlueView.setBackgroundColor(Color.BLUE); - overlapLayout.addView(rightBlueView, rightViewLayoutParams); - - parent.addView(overlapLayout, new LinearLayout.LayoutParams( - totalSize, surfaceViewSize)); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mSurfaceView = new SurfaceView(this); - mSurfaceView.getHolder().addCallback(this); - mSurfaceView.setAlpha(mAlpha); - - LinearLayout content = new LinearLayout(this); - content.setOrientation(LinearLayout.VERTICAL); - - TextView alphaText = new TextView(this); - alphaText.setText(getAlphaText()); - - SeekBar alphaToggle = new SeekBar(this); - alphaToggle.setMin(0); - alphaToggle.setMax(255); - alphaToggle.setProgress(Math.round(mAlpha * 255)); - alphaToggle.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - mAlpha = progress / 255f; - alphaText.setText(getAlphaText()); - mSurfaceView.setAlpha(mAlpha); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - - } - }); - - content.addView(alphaText, new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.WRAP_CONTENT, - LinearLayout.LayoutParams.WRAP_CONTENT)); - - content.addView(alphaToggle, new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.WRAP_CONTENT)); - - Button button = new Button(this); - button.setText("Z " + mZOrder.toString()); - button.setOnClickListener(v -> { - toggleZOrder(); - mSurfaceView.setZOrderOnTop(ZOrder.ABOVE.equals(mZOrder)); - button.setText("Z " + mZOrder.toString()); - }); - - content.addView(button, new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.WRAP_CONTENT, - LinearLayout.LayoutParams.WRAP_CONTENT)); - - overlapViews(mSurfaceView, content); - - setContentView(content); - } - - @Override - public void surfaceCreated(SurfaceHolder holder) { - } - - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - Canvas canvas = holder.lockCanvas(); - canvas.drawColor(Color.RED); - holder.unlockCanvasAndPost(canvas); - } - - @Override - public void surfaceDestroyed(SurfaceHolder holder) { - } -} |