diff options
235 files changed, 3957 insertions, 2497 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java index ebfda527001d..010006edc995 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java @@ -809,7 +809,11 @@ public final class JobServiceContext implements ServiceConnection { if (!verifyCallerLocked(cb)) { return; } - + if (mVerb != VERB_EXECUTING) { + // Any state other than executing means the + // job is in transient or stopped state + return; + } executing = getRunningJobLocked(); } if (executing != null && jobId == executing.getJobId()) { diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h index 1f15daf1ba47..335dc97bf964 100644 --- a/cmds/idmap2/include/idmap2/Idmap.h +++ b/cmds/idmap2/include/idmap2/Idmap.h @@ -178,8 +178,8 @@ class IdmapHeader { }; struct IdmapConstraint { - // Constraint type can be TYPE_DISPLAY_ID or TYP_DEVICE_ID, please refer - // to ConstraintType in OverlayConstraint.java + // Constraint type can be android::kOverlayConstraintTypeDisplayId or + // android::kOverlayConstraintTypeDeviceId uint32_t constraint_type; uint32_t constraint_value; diff --git a/core/api/current.txt b/core/api/current.txt index f5dcf2de4c51..9ebb5068bf19 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -18112,6 +18112,7 @@ package android.graphics.drawable { method public void setThickness(@Px int); method public void setThicknessRatio(@FloatRange(from=0.0f, fromInclusive=false) float); method public void setUseLevel(boolean); + field @FlaggedApi("com.android.graphics.flags.gradient_drawable_shape_rounded_cap") public static final int ARC = 4; // 0x4 field public static final int LINE = 2; // 0x2 field public static final int LINEAR_GRADIENT = 0; // 0x0 field public static final int OVAL = 1; // 0x1 diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 0519695ff7fe..1a6e9b07067f 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2976,6 +2976,13 @@ class ContextImpl extends Context { if (display != null) { updateDeviceIdIfChanged(display.getDisplayId()); } + updateResourceOverlayConstraints(); + } + + private void updateResourceOverlayConstraints() { + if (mResources != null) { + mResources.getAssets().setOverlayConstraints(getDisplayId(), getDeviceId()); + } } @Override @@ -2988,9 +2995,11 @@ class ContextImpl extends Context { } } - return new ContextImpl(this, mMainThread, mPackageInfo, mParams, + final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mParams, mAttributionSource.getAttributionTag(), mAttributionSource.getNext(), mSplitName, mToken, mUser, mFlags, mClassLoader, null, deviceId, true); + context.updateResourceOverlayConstraints(); + return context; } @NonNull @@ -3285,6 +3294,7 @@ class ContextImpl extends Context { mDeviceId = updatedDeviceId; mAttributionSource = createAttributionSourceWithDeviceId(mAttributionSource, mDeviceId); notifyOnDeviceChangedListeners(updatedDeviceId); + updateResourceOverlayConstraints(); } } @@ -3700,6 +3710,7 @@ class ContextImpl extends Context { mResourcesManager.setLocaleConfig(lc); } } + updateResourceOverlayConstraints(); } void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) { diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig index ba1473cf5ed7..67ade79e1b94 100644 --- a/core/java/android/companion/virtual/flags/flags.aconfig +++ b/core/java/android/companion/virtual/flags/flags.aconfig @@ -135,3 +135,11 @@ flag { description: "Show virtual devices in Settings" bug: "338974320" } + +flag { + name: "migrate_viewconfiguration_constants_to_resources" + namespace: "virtual_devices" + description: "Use resources instead of constants in ViewConfiguration" + is_fixed_read_only: true + bug: "370928384" +} diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index f91b2474fdac..9e91f5944504 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2038,7 +2038,7 @@ public abstract class PackageManager { public static final int INSTALL_SCENARIO_DEFAULT = 0; /** - * Installation scenario providing the fastest “install button to launch" experience possible. + * Installation scenario providing the fastest "install button to launch" experience possible. */ public static final int INSTALL_SCENARIO_FAST = 1; @@ -3585,7 +3585,7 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device is - * compatible with Android’s security model. + * compatible with Android's security model. * * <p>See sections 2 and 9 in the * <a href="https://source.android.com/compatibility/android-cdd">Android CDD</a> for more diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig index e4b8c90d381d..255a08cf170f 100644 --- a/core/java/android/content/pm/flags.aconfig +++ b/core/java/android/content/pm/flags.aconfig @@ -391,3 +391,12 @@ flag { bug: "319137634" is_fixed_read_only: true } + +flag { + name: "always_load_past_certs_v4" + is_exported: true + namespace: "package_manager_service" + description: "Always read the corresponding v3/3.1 signature block for the current v4 to get the past rotated certificates, even when not verifying integrity." + bug: "378539511" + is_fixed_read_only: true +} diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 7cd2d31ac974..bcb50881d327 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -148,8 +148,8 @@ public final class AssetManager implements AutoCloseable { * @hide */ public static class Builder { - private ArrayList<ApkAssets> mUserApkAssets = new ArrayList<>(); - private ArrayList<ResourcesLoader> mLoaders = new ArrayList<>(); + private final ArrayList<ApkAssets> mUserApkAssets = new ArrayList<>(); + private final ArrayList<ResourcesLoader> mLoaders = new ArrayList<>(); private boolean mNoInit = false; @@ -1625,6 +1625,23 @@ public final class AssetManager implements AutoCloseable { } /** + * Passes the display id and device id to AssetManager, to filter out overlays based on + * any {@link android.content.om.OverlayConstraint}. + * + * @hide + */ + public void setOverlayConstraints(int displayId, int deviceId) { + if (!Flags.rroConstraints()) { + return; + } + + synchronized (this) { + ensureValidLocked(); + nativeSetOverlayConstraints(mObject, displayId, deviceId); + } + } + + /** * @hide */ @UnsupportedAppUsage @@ -1717,6 +1734,7 @@ public final class AssetManager implements AutoCloseable { int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout, int uiMode, int colorMode, int grammaticalGender, int majorVersion, boolean forceRefresh); + private static native void nativeSetOverlayConstraints(long ptr, int displayId, int deviceId); private static native @NonNull SparseArray<String> nativeGetAssignedPackageIdentifiers( long ptr, boolean includeOverlays, boolean includeLoaders); diff --git a/core/java/android/os/PerfettoTrackEventExtra.java b/core/java/android/os/PerfettoTrackEventExtra.java index adb98aa25f8f..68442293c3a3 100644 --- a/core/java/android/os/PerfettoTrackEventExtra.java +++ b/core/java/android/os/PerfettoTrackEventExtra.java @@ -23,6 +23,8 @@ import dalvik.annotation.optimization.FastNative; import libcore.util.NativeAllocationRegistry; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Supplier; @@ -50,6 +52,7 @@ public final class PerfettoTrackEventExtra { private static final Supplier<FieldString> sFieldStringSupplier = FieldString::new; private static final Supplier<FieldNested> sFieldNestedSupplier = FieldNested::new; + private final List<PerfettoPointer> mPendingPointers = new ArrayList<>(); private CounterInt64 mCounterInt64; private CounterDouble mCounterDouble; private Proto mProto; @@ -592,7 +595,7 @@ public final class PerfettoTrackEventExtra { checkContainer(); FieldInt64 field = mFieldInt64Cache.get(sFieldInt64Supplier); field.setValue(id, val); - mCurrentContainer.addField(field); + mExtra.addPerfettoPointer(mCurrentContainer, field); return this; } @@ -601,7 +604,7 @@ public final class PerfettoTrackEventExtra { checkContainer(); FieldDouble field = mFieldDoubleCache.get(sFieldDoubleSupplier); field.setValue(id, val); - mCurrentContainer.addField(field); + mExtra.addPerfettoPointer(mCurrentContainer, field); return this; } @@ -610,7 +613,7 @@ public final class PerfettoTrackEventExtra { checkContainer(); FieldString field = mFieldStringCache.get(sFieldStringSupplier); field.setValue(id, val); - mCurrentContainer.addField(field); + mExtra.addPerfettoPointer(mCurrentContainer, field); return this; } @@ -635,7 +638,7 @@ public final class PerfettoTrackEventExtra { checkContainer(); FieldNested field = mFieldNestedCache.get(sFieldNestedSupplier); field.setId(id); - mCurrentContainer.addField(field); + mExtra.addPerfettoPointer(mCurrentContainer, field); return mBuilderCache.get(sBuilderSupplier).initInternal(this, field); } @@ -735,6 +738,15 @@ public final class PerfettoTrackEventExtra { */ public void addPerfettoPointer(PerfettoPointer extra) { native_add_arg(mPtr, extra.getPtr()); + mPendingPointers.add(extra); + } + + /** + * Adds a pointer representing a track event parameter to the {@code container}. + */ + public void addPerfettoPointer(FieldContainer container, PerfettoPointer extra) { + container.addField(extra); + mPendingPointers.add(extra); } /** @@ -742,6 +754,7 @@ public final class PerfettoTrackEventExtra { */ public void reset() { native_clear_args(mPtr); + mPendingPointers.clear(); } private CounterInt64 getCounterInt64() { diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 6f94c1b2d274..4cbd5beb3a8c 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -3131,6 +3131,7 @@ public class ZenModeConfig implements Parcelable { * @return null if DND is off or describeForeverCondition is false and * DND is on forever (until turned off) */ + // TODO: b/368247671 - Delete when inlining MODES_UI public static String getDescription(Context context, boolean zenOn, ZenModeConfig config, boolean describeForeverCondition) { if (!zenOn || config == null) { diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 2fa56137a8a0..b273a7f7c271 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -1071,13 +1071,27 @@ public abstract class Layout { var newBackground = determineContrastingBackgroundColor(index); var hasBgColorChanged = newBackground != bgPaint.getColor(); - if (lineNum != mLastLineNum || hasBgColorChanged) { - // Skip processing if the character is a space or a tap to avoid - // rendering an abrupt, empty rectangle. - if (Character.isWhitespace(mText.charAt(index))) { - return; - } + // Skip processing if the character is a space or a tap to avoid + // rendering an abrupt, empty rectangle. + if (TextLine.isLineEndSpace(mText.charAt(index))) { + return; + } + // To avoid highlighting emoji sequences, we use Extended_Pictgraphs as a + // heuristic. Highlighting is skipped based on code points, not glyph type + // (text vs. color), so emojis with default text presentation are + // intentionally not highlighted (numeric representation with emoji + // presentation are manually excluded). Although we process ZWJ and + // variation selectors within emoji sequences, they should not affect + // highlighting due to their zero-width nature. + var codePoint = Character.codePointAt(mText, index); + var isEmoji = Character.isEmojiComponent(codePoint) + || Character.isExtendedPictographic(codePoint); + if (isEmoji && !isStandardNumber(index)) { + return; + } + + if (lineNum != mLastLineNum || hasBgColorChanged) { // Draw what we have so far, then reset the rect and update its color drawRect(); mLineBackground.set(left, top, right, bottom); @@ -1096,6 +1110,16 @@ public abstract class Layout { drawRect(); } + private boolean isStandardNumber(int index) { + var codePoint = Character.codePointAt(mText, index); + var isNumberSignOrAsterisk = (codePoint >= '0' && codePoint <= '9') + || codePoint == '#' || codePoint == '*'; + var isColoredGlyph = index + 1 < mText.length() + && Character.codePointAt(mText, index + 1) == 0xFE0F; + + return isNumberSignOrAsterisk && !isColoredGlyph; + } + private void drawRect() { if (!mLineBackground.isEmpty()) { mLineBackground.inset(-padding, -padding); @@ -4626,6 +4650,16 @@ public abstract class Layout { * Callback for {@link #forEachCharacterBounds(int, int, int, int, CharacterBoundsListener)} */ private interface CharacterBoundsListener { + /** + * Called for each character with its bounds. + * + * @param index the index of the character + * @param lineNum the line number of the character + * @param left the left edge of the character + * @param top the top edge of the character + * @param right the right edge of the character + * @param bottom the bottom edge of the character + */ void onCharacterBounds(int index, int lineNum, float left, float top, float right, float bottom); diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java index a4c3ed96f2ce..5910434dc692 100644 --- a/core/java/android/util/apk/ApkSignatureVerifier.java +++ b/core/java/android/util/apk/ApkSignatureVerifier.java @@ -260,9 +260,11 @@ public class ApkSignatureVerifier { Certificate[][] nonstreamingCerts = null; int v3BlockId = APK_SIGNATURE_SCHEME_DEFAULT; - // If V4 contains additional signing blocks then we need to always run v2/v3 verifier - // to figure out which block they use. - if (verifyFull || signingInfos.signingInfoBlocks.length > 0) { + // We need to always run v2/v3 verifier to figure out which block they use so we can + // return the past signers as well as the current one - the rotation chain is important + // for many callers who verify the signature origin as well as the apk integrity. + if (android.content.pm.Flags.alwaysLoadPastCertsV4() + || verifyFull || signingInfos.signingInfoBlocks.length > 0) { try { // v4 is an add-on and requires v2 or v3 signature to validate against its // certificate and digest diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java index df680c054f56..fe868e1c43be 100644 --- a/core/java/android/view/NotificationHeaderView.java +++ b/core/java/android/view/NotificationHeaderView.java @@ -223,8 +223,14 @@ public class NotificationHeaderView extends RelativeLayout { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (notificationsRedesignTemplates()) { - mTopLineTranslation = measureCenterTranslation(mTopLineView); - mExpandButtonTranslation = measureCenterTranslation(mExpandButton); + // TODO: b/378660052 - These should never be null in practice, consider using + // requireViewById() in the onFinishInflate. + if (mTopLineView != null) { + mTopLineTranslation = measureCenterTranslation(mTopLineView); + } + if (mExpandButton != null) { + mExpandButtonTranslation = measureCenterTranslation(mExpandButton); + } } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 0866e0d832b1..c048d7987515 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -66,6 +66,7 @@ import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFI import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION; import static com.android.window.flags.Flags.FLAG_DELEGATE_UNHANDLED_DRAGS; import static com.android.window.flags.Flags.FLAG_SUPPORTS_DRAG_ASSISTANT_TO_MULTIWINDOW; +import static com.android.window.flags.Flags.reduceChangedExclusionRectsMsgs; import static java.lang.Math.max; @@ -247,6 +248,7 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; @@ -12888,15 +12890,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (rects.isEmpty() && mListenerInfo == null) return; final ListenerInfo info = getListenerInfo(); + final boolean rectsChanged = !reduceChangedExclusionRectsMsgs() + || !Objects.equals(info.mSystemGestureExclusionRects, rects); if (info.mSystemGestureExclusionRects != null) { - info.mSystemGestureExclusionRects.clear(); - info.mSystemGestureExclusionRects.addAll(rects); + if (rectsChanged) { + info.mSystemGestureExclusionRects.clear(); + info.mSystemGestureExclusionRects.addAll(rects); + } } else { info.mSystemGestureExclusionRects = new ArrayList<>(rects); } - - updatePositionUpdateListener(); - postUpdate(this::updateSystemGestureExclusionRects); + if (rectsChanged) { + updatePositionUpdateListener(); + postUpdate(this::updateSystemGestureExclusionRects); + } } private void updatePositionUpdateListener() { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index cfdcc10a20f4..9498407fb33b 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -136,6 +136,7 @@ import static com.android.text.flags.Flags.disableHandwritingInitiatorForIme; import static com.android.window.flags.Flags.enableBufferTransformHintFromDisplay; import static com.android.window.flags.Flags.enableWindowContextResourcesUpdateOnConfigChange; import static com.android.window.flags.Flags.predictiveBackSwipeEdgeNoneApi; +import static com.android.window.flags.Flags.reduceChangedExclusionRectsMsgs; import static com.android.window.flags.Flags.setScPropertiesInClient; import android.Manifest; @@ -152,8 +153,6 @@ import android.annotation.UiContext; import android.app.ActivityManager; import android.app.ActivityThread; import android.app.ResourcesManager; -import android.app.UiModeManager; -import android.app.UiModeManager.ForceInvertStateChangeListener; import android.app.WindowConfiguration; import android.app.compat.CompatChanges; import android.app.servertransaction.WindowStateTransactionItem; @@ -169,6 +168,7 @@ import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; +import android.database.ContentObserver; import android.graphics.BLASTBufferQueue; import android.graphics.Canvas; import android.graphics.Color; @@ -214,6 +214,7 @@ import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.os.Vibrator; +import android.provider.Settings; import android.sysprop.DisplayProperties; import android.sysprop.ViewProperties; import android.text.TextUtils; @@ -469,8 +470,10 @@ public final class ViewRootImpl implements ViewParent, private CompatOnBackInvokedCallback mCompatOnBackInvokedCallback; @Nullable - private ForceInvertStateChangeListener mForceInvertStateChangeListener; + private ContentObserver mForceInvertObserver; + private static final int INVALID_VALUE = Integer.MIN_VALUE; + private int mForceInvertEnabled = INVALID_VALUE; /** * Callback for notifying about global configuration changes. */ @@ -552,8 +555,6 @@ public final class ViewRootImpl implements ViewParent, @UiContext public final Context mContext; - private UiModeManager mUiModeManager; - @UnsupportedAppUsage final IWindowSession mWindowSession; @NonNull Display mDisplay; @@ -1255,7 +1256,6 @@ public final class ViewRootImpl implements ViewParent, public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session, WindowLayout windowLayout) { mContext = context; - mUiModeManager = context.getSystemService(UiModeManager.class); mWindowSession = session; mWindowLayout = windowLayout; mDisplay = display; @@ -1804,6 +1804,23 @@ public final class ViewRootImpl implements ViewParent, } } + private boolean isForceInvertEnabled() { + if (mForceInvertEnabled == INVALID_VALUE) { + reloadForceInvertEnabled(); + } + return mForceInvertEnabled == 1; + } + + private void reloadForceInvertEnabled() { + if (forceInvertColor()) { + mForceInvertEnabled = Settings.Secure.getIntForUser( + mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, + /* def= */ 0, + UserHandle.myUserId()); + } + } + /** * Register any kind of listeners if setView was success. */ @@ -1835,11 +1852,21 @@ public final class ViewRootImpl implements ViewParent, mBasePackageName); if (forceInvertColor()) { - if (mForceInvertStateChangeListener == null) { - mForceInvertStateChangeListener = - forceInvertState -> updateForceDarkMode(); - mUiModeManager.addForceInvertStateChangeListener(mExecutor, - mForceInvertStateChangeListener); + if (mForceInvertObserver == null) { + mForceInvertObserver = new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange) { + reloadForceInvertEnabled(); + updateForceDarkMode(); + } + }; + mContext.getContentResolver().registerContentObserver( + Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED + ), + false, + mForceInvertObserver, + UserHandle.myUserId()); } } } @@ -1857,10 +1884,9 @@ public final class ViewRootImpl implements ViewParent, .unregisterDisplayListener(mDisplayListener); if (forceInvertColor()) { - if (mForceInvertStateChangeListener != null) { - mUiModeManager.removeForceInvertStateChangeListener( - mForceInvertStateChangeListener); - mForceInvertStateChangeListener = null; + if (mForceInvertObserver != null) { + mContext.getContentResolver().unregisterContentObserver(mForceInvertObserver); + mForceInvertObserver = null; } } @@ -2047,25 +2073,21 @@ public final class ViewRootImpl implements ViewParent, return getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; } - /** - * Determines the type of force dark to apply, considering force inversion, system night mode, - * and app-specific settings (including developer opt-outs). - * - * @return A {@link ForceDarkType.ForceDarkTypeDef} constant indicating the force dark type. - */ + /** Returns true if force dark should be enabled according to various settings */ @VisibleForTesting public @ForceDarkType.ForceDarkTypeDef int determineForceDarkType() { if (forceInvertColor()) { // Force invert ignores all developer opt-outs. // We also ignore dark theme, since the app developer can override the user's preference - // for dark mode in configuration.uiMode. Instead, we assume that both force invert and - // the system's dark theme are enabled. - if (mUiModeManager.getForceInvertState() == UiModeManager.FORCE_INVERT_TYPE_DARK) { + // for dark mode in configuration.uiMode. Instead, we assume that the force invert + // setting will be enabled at the same time dark theme is in the Settings app. + if (isForceInvertEnabled()) { return ForceDarkType.FORCE_INVERT_COLOR_DARK; } } boolean useAutoDark = getNightMode() == Configuration.UI_MODE_NIGHT_YES; + if (useAutoDark) { boolean forceDarkAllowedDefault = SystemProperties.getBoolean(ThreadedRenderer.DEBUG_FORCE_DARK, false); @@ -6053,8 +6075,12 @@ public final class ViewRootImpl implements ViewParent, } void updateSystemGestureExclusionRectsForView(View view) { + boolean msgInQueue = reduceChangedExclusionRectsMsgs() + && mGestureExclusionTracker.isWaitingForComputeChanges(); mGestureExclusionTracker.updateRectsForView(view); - mHandler.sendEmptyMessage(MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED); + if (!msgInQueue) { + mHandler.sendEmptyMessage(MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED); + } } void systemGestureExclusionChanged() { @@ -6098,8 +6124,12 @@ public final class ViewRootImpl implements ViewParent, * the root's view hierarchy. */ public void setRootSystemGestureExclusionRects(@NonNull List<Rect> rects) { + boolean msgInQueue = reduceChangedExclusionRectsMsgs() + && mGestureExclusionTracker.isWaitingForComputeChanges(); mGestureExclusionTracker.setRootRects(rects); - mHandler.sendEmptyMessage(MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED); + if (!msgInQueue) { + mHandler.sendEmptyMessage(MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED); + } } /** diff --git a/core/java/android/view/ViewRootRectTracker.java b/core/java/android/view/ViewRootRectTracker.java index 152729b8d1d8..0bef3255f1dc 100644 --- a/core/java/android/view/ViewRootRectTracker.java +++ b/core/java/android/view/ViewRootRectTracker.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Rect; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import java.lang.ref.WeakReference; @@ -32,8 +33,10 @@ import java.util.function.Function; /** * Abstract class to track a collection of rects reported by the views under the same * {@link ViewRootImpl}. + * @hide */ -class ViewRootRectTracker { +@VisibleForTesting +public class ViewRootRectTracker { private final Function<View, List<Rect>> mRectCollector; private boolean mViewsChanged = false; private boolean mRootRectsChanged = false; @@ -41,11 +44,18 @@ class ViewRootRectTracker { private List<ViewInfo> mViewInfos = new ArrayList<>(); private List<Rect> mRects = Collections.emptyList(); + // Keeps track of whether updateRectsForView has been called but there was no subsequent call + // on computeChanges yet. Since updateRectsForView is called when sending a message and + // computeChanges when it is received, this tracks whether such message is in the queue already + private boolean mWaitingForComputeChanges = false; + /** * @param rectCollector given a view returns a list of the rects of interest for this * ViewRootRectTracker + * @hide */ - ViewRootRectTracker(Function<View, List<Rect>> rectCollector) { + @VisibleForTesting + public ViewRootRectTracker(Function<View, List<Rect>> rectCollector) { mRectCollector = rectCollector; } @@ -70,6 +80,7 @@ class ViewRootRectTracker { mViewInfos.add(new ViewInfo(view)); mViewsChanged = true; } + mWaitingForComputeChanges = true; } /** @@ -92,6 +103,7 @@ class ViewRootRectTracker { * @return {@code true} if there were changes, {@code false} otherwise. */ public boolean computeChanges() { + mWaitingForComputeChanges = false; boolean changed = mRootRectsChanged; final Iterator<ViewInfo> i = mViewInfos.iterator(); final List<Rect> rects = new ArrayList<>(mRootRects); @@ -121,6 +133,10 @@ class ViewRootRectTracker { return false; } + public boolean isWaitingForComputeChanges() { + return mWaitingForComputeChanges; + } + /** * Returns a List of all Rects from all visible Views in the global (root) coordinate system. * This list is only updated when calling {@link #computeChanges()} or @@ -140,6 +156,7 @@ class ViewRootRectTracker { Preconditions.checkNotNull(rects, "rects must not be null"); mRootRects = rects; mRootRectsChanged = true; + mWaitingForComputeChanges = true; } @NonNull diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig index 891c5e382a58..3e3b8e100d6d 100644 --- a/core/java/android/window/flags/lse_desktop_experience.aconfig +++ b/core/java/android/window/flags/lse_desktop_experience.aconfig @@ -196,6 +196,16 @@ flag { } flag { + name: "enable_camera_compat_for_desktop_windowing_opt_out" + namespace: "lse_desktop_experience" + description: "Whether to allow developers to opt-out of Camera Compat treatment to fixed-orientation apps in desktop windowing mode" + bug: "328616176" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "enable_task_stack_observer_in_shell" namespace: "lse_desktop_experience" description: "Introduces a new observer in shell to track the task stack." diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig index 60f2c811dd1f..a4d128fa3caf 100644 --- a/core/java/android/window/flags/windowing_frontend.aconfig +++ b/core/java/android/window/flags/windowing_frontend.aconfig @@ -428,6 +428,17 @@ flag { } flag { + name: "reduce_changed_exclusion_rects_msgs" + namespace: "windowing_frontend" + description: "Don't send MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED when there is no change" + bug: "388231176" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "keep_app_window_hide_while_locked" namespace: "windowing_frontend" description: "Do not let app window visible while device is locked" diff --git a/core/java/com/android/internal/app/MediaRouteControllerContentManager.java b/core/java/com/android/internal/app/MediaRouteControllerContentManager.java index 83ae7edd796b..dc4caa3d35d7 100644 --- a/core/java/com/android/internal/app/MediaRouteControllerContentManager.java +++ b/core/java/com/android/internal/app/MediaRouteControllerContentManager.java @@ -28,19 +28,40 @@ import com.android.internal.R; * This class manages the content display within the media route controller UI. */ public class MediaRouteControllerContentManager { + /** + * A delegate interface that a MediaRouteController UI should implement. It allows the content + * manager to inform the UI of any UI changes that need to be made in response to content + * updates. + */ + public interface Delegate { + /** + * Updates the title of the cast device + */ + void setCastDeviceTitle(CharSequence title); + + /** + * Dismiss the UI to transition to a different workflow. + */ + void dismissView(); + } + + private final Delegate mDelegate; + // Time to wait before updating the volume when the user lets go of the seek bar // to allow the route provider time to propagate the change and publish a new // route descriptor. private static final int VOLUME_UPDATE_DELAY_MILLIS = 250; + private final MediaRouter mRouter; private final MediaRouter.RouteInfo mRoute; private LinearLayout mVolumeLayout; private SeekBar mVolumeSlider; private boolean mVolumeSliderTouched; - public MediaRouteControllerContentManager(Context context) { - MediaRouter mRouter = context.getSystemService(MediaRouter.class); + public MediaRouteControllerContentManager(Context context, Delegate delegate) { + mDelegate = delegate; + mRouter = context.getSystemService(MediaRouter.class); mRoute = mRouter.getSelectedRoute(); } @@ -49,6 +70,7 @@ public class MediaRouteControllerContentManager { * given container view. */ public void bindViews(View containerView) { + mDelegate.setCastDeviceTitle(mRoute.getName()); mVolumeLayout = containerView.findViewById(R.id.media_route_volume_layout); mVolumeSlider = containerView.findViewById(R.id.media_route_volume_slider); mVolumeSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @@ -89,6 +111,14 @@ public class MediaRouteControllerContentManager { } /** + * Updates all the views to reflect new states. + */ + public void update() { + mDelegate.setCastDeviceTitle(mRoute.getName()); + updateVolume(); + } + + /** * Updates the volume layout and slider. */ public void updateVolume() { @@ -103,6 +133,20 @@ public class MediaRouteControllerContentManager { } } + /** + * Callback function to triggered after the disconnect button is clicked. + */ + public void onDisconnectButtonClick() { + if (mRoute.isSelected()) { + if (mRoute.isBluetooth()) { + mRouter.getDefaultRoute().select(); + } else { + mRouter.getFallbackRoute().select(); + } + } + mDelegate.dismissView(); + } + private boolean isVolumeControlAvailable() { return mRoute.getVolumeHandling() == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE; } diff --git a/core/java/com/android/internal/app/MediaRouteControllerDialog.java b/core/java/com/android/internal/app/MediaRouteControllerDialog.java index c79f3c7bf76f..45a4a13667a4 100644 --- a/core/java/com/android/internal/app/MediaRouteControllerDialog.java +++ b/core/java/com/android/internal/app/MediaRouteControllerDialog.java @@ -46,7 +46,10 @@ import com.android.internal.R; * * TODO: Move this back into the API, as in the support library media router. */ -public class MediaRouteControllerDialog extends AlertDialog { +public class MediaRouteControllerDialog extends AlertDialog implements + MediaRouteControllerContentManager.Delegate { + // TODO(b/360050020): Eventually these 3 variables should be in the content manager instead of + // here. So these should be removed when the migration is completed. private final MediaRouter mRouter; private final MediaRouterCallback mCallback; private final MediaRouter.RouteInfo mRoute; @@ -63,7 +66,7 @@ public class MediaRouteControllerDialog extends AlertDialog { public MediaRouteControllerDialog(Context context, int theme) { super(context, theme); - mContentManager = new MediaRouteControllerContentManager(context); + mContentManager = new MediaRouteControllerContentManager(context, this); mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE); mCallback = new MediaRouterCallback(); mRoute = mRouter.getSelectedRoute(); @@ -71,24 +74,13 @@ public class MediaRouteControllerDialog extends AlertDialog { @Override protected void onCreate(Bundle savedInstanceState) { - setTitle(mRoute.getName()); Resources res = getContext().getResources(); setButton(BUTTON_NEGATIVE, res.getString(R.string.media_route_controller_disconnect), - (dialogInterface, id) -> { - if (mRoute.isSelected()) { - if (mRoute.isBluetooth()) { - mRouter.getDefaultRoute().select(); - } else { - mRouter.getFallbackRoute().select(); - } - } - dismiss(); - }); + (dialogInterface, id) -> mContentManager.onDisconnectButtonClick()); View customView = getLayoutInflater().inflate(R.layout.media_route_controller_dialog, null); setView(customView, 0, 0, 0, 0); - super.onCreate(savedInstanceState); - mContentManager.bindViews(customView); + super.onCreate(savedInstanceState); View customPanelView = getWindow().findViewById(R.id.customPanel); if (customPanelView != null) { @@ -135,13 +127,22 @@ public class MediaRouteControllerDialog extends AlertDialog { return super.onKeyUp(keyCode, event); } + @Override + public void setCastDeviceTitle(CharSequence title) { + setTitle(title); + } + + @Override + public void dismissView() { + dismiss(); + } + private void update() { if (!mRoute.isSelected() || mRoute.isDefault()) { - dismiss(); + dismissView(); } - setTitle(mRoute.getName()); - mContentManager.updateVolume(); + mContentManager.update(); Drawable icon = getIconDrawable(); if (icon != mCurrentIconDrawable) { diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java index 2931bd2c83dd..fe616e085488 100644 --- a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java +++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java @@ -16,6 +16,7 @@ package com.android.internal.os; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import android.ravenwood.annotation.RavenwoodKeepWholeClass; @@ -155,8 +156,9 @@ public final class LongArrayMultiStateCounter implements Parcelable { /** * Adds the supplied values to the current accumulated values in the counter. + * Null `values` parameter is interpreted as an array of zeros. */ - public void incrementValues(long[] values, long timestampMs) { + public void incrementValues(@Nullable long[] values, long timestampMs) { native_incrementValues(mNativeObject, values, timestampMs); } diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter_ravenwood.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter_ravenwood.java index 7030d8e84b70..4f5f37d0640e 100644 --- a/core/java/com/android/internal/os/LongArrayMultiStateCounter_ravenwood.java +++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter_ravenwood.java @@ -16,6 +16,7 @@ package com.android.internal.os; +import android.annotation.Nullable; import android.os.BadParcelableException; import android.os.Parcel; import android.ravenwood.annotation.RavenwoodKeepWholeClass; @@ -147,10 +148,12 @@ class LongArrayMultiStateCounter_ravenwood { mLastUpdateTimestampMs = timestampMs; } - public void incrementValues(long[] delta, long timestampMs) { + public void incrementValues(@Nullable long[] delta, long timestampMs) { long[] values = Arrays.copyOf(mValues, mValues.length); - for (int i = 0; i < mArrayLength; i++) { - values[i] += delta[i]; + if (delta != null) { + for (int i = 0; i < mArrayLength; i++) { + values[i] += delta[i]; + } } updateValue(values, timestampMs); } @@ -304,7 +307,8 @@ class LongArrayMultiStateCounter_ravenwood { getInstance(targetInstanceId).copyStatesFrom(getInstance(sourceInstanceId)); } - public static void native_incrementValues(long instanceId, long[] delta, long timestampMs) { + public static void native_incrementValues(long instanceId, @Nullable long[] delta, + long timestampMs) { getInstance(instanceId).incrementValues(delta, timestampMs); } diff --git a/core/java/com/android/internal/os/PowerStats.java b/core/java/com/android/internal/os/PowerStats.java index aafef6c8b5fd..6c69e2c623ad 100644 --- a/core/java/com/android/internal/os/PowerStats.java +++ b/core/java/com/android/internal/os/PowerStats.java @@ -128,6 +128,7 @@ public final class PowerStats { * Extra parameters specific to the power component, e.g. the availability of power * monitors. */ + @NonNull public final PersistableBundle extras; private PowerStatsFormatter mDeviceStatsFormatter; @@ -269,20 +270,41 @@ public final class PowerStats { stateStatsArrayLength, uidStatsArrayLength, extras); } + @SuppressWarnings("deprecation") @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Descriptor)) return false; Descriptor that = (Descriptor) o; - return powerComponentId == that.powerComponentId - && statsArrayLength == that.statsArrayLength - && stateLabels.contentEquals(that.stateLabels) - && stateStatsArrayLength == that.stateStatsArrayLength - && uidStatsArrayLength == that.uidStatsArrayLength - && Objects.equals(name, that.name) - && extras.size() == that.extras.size() // Unparcel the Parcel if not yet - && Bundle.kindofEquals(extras, - that.extras); // Since the Parcel is now unparceled, do a deep comparison + if (powerComponentId != that.powerComponentId + || statsArrayLength != that.statsArrayLength + || !stateLabels.contentEquals(that.stateLabels) + || stateStatsArrayLength != that.stateStatsArrayLength + || uidStatsArrayLength != that.uidStatsArrayLength + || !Objects.equals(name, that.name)) { + return false; + } + + // Getting the size has the side-effect of unparceling the Bundle if not yet + if (extras.size() != that.extras.size()) { + return false; + } + + if (Bundle.kindofEquals(extras, that.extras)) { + return true; + } + + // Since `kindofEquals` does not deep-compare arrays, we do that separately, albeit at + // the expense of creating an iterator and using a deprecated API, `bundle.get`. + // There is no performance concern, because the situation where PowerStatsDescriptors + // are changed in an incompatible way are exceedingly rare, occurring at most + // once per power component after a system upgrade. + for (String key : extras.keySet()) { + if (!Objects.deepEquals(extras.get(key), that.extras.get(key))) { + return false; + } + } + return true; } /** diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedComponentImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedComponentImpl.java index 7ee22f30ace0..69c04807c604 100644 --- a/core/java/com/android/internal/pm/pkg/component/ParsedComponentImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedComponentImpl.java @@ -157,7 +157,7 @@ public abstract class ParsedComponentImpl implements ParsedComponent, Parcelable @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeString(this.name); + sForInternedString.parcel(this.name, dest, flags); dest.writeInt(this.getIcon()); dest.writeInt(this.getLabelRes()); dest.writeCharSequence(this.getNonLocalizedLabel()); @@ -175,7 +175,7 @@ public abstract class ParsedComponentImpl implements ParsedComponent, Parcelable // We use the boot classloader for all classes that we load. final ClassLoader boot = Object.class.getClassLoader(); //noinspection ConstantConditions - this.name = in.readString(); + this.name = sForInternedString.unparcel(in); this.icon = in.readInt(); this.labelRes = in.readInt(); this.nonLocalizedLabel = in.readCharSequence(); diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java index 93be3b02a12a..2d989943800e 100644 --- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java +++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java @@ -322,7 +322,7 @@ public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implemen PrintWriter pw = shell.getOutPrintWriter(); if (android.tracing.Flags.clientSideProtoLogging()) { - pw.println("Command deprecated. Please use 'cmd protolog' instead."); + pw.println("Command deprecated. Please use 'cmd protolog_configuration' instead."); return -1; } diff --git a/core/java/com/android/internal/protolog/ProtoLogCommandHandler.java b/core/java/com/android/internal/protolog/ProtoLogCommandHandler.java index 82d8d3431a9d..6d4a40899a65 100644 --- a/core/java/com/android/internal/protolog/ProtoLogCommandHandler.java +++ b/core/java/com/android/internal/protolog/ProtoLogCommandHandler.java @@ -145,11 +145,11 @@ public class ProtoLogCommandHandler extends ShellCommand { switch (cmd) { case "enable" -> { - mProtoLogConfigurationService.enableProtoLogToLogcat(processGroups()); + mProtoLogConfigurationService.enableProtoLogToLogcat(pw, processGroups()); return 0; } case "disable" -> { - mProtoLogConfigurationService.disableProtoLogToLogcat(processGroups()); + mProtoLogConfigurationService.disableProtoLogToLogcat(pw, processGroups()); return 0; } default -> { diff --git a/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java b/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java index d65aaae7deaa..a19690bbd0e4 100644 --- a/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java +++ b/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java @@ -18,6 +18,8 @@ package com.android.internal.protolog; import android.annotation.NonNull; +import java.io.PrintWriter; + public interface ProtoLogConfigurationService extends IProtoLogConfigurationService { /** * Get the list of groups clients have registered to the protolog service. @@ -37,11 +39,11 @@ public interface ProtoLogConfigurationService extends IProtoLogConfigurationServ * Enable logging target groups to logcat. * @param groups we want to enable logging them to logcat for. */ - void enableProtoLogToLogcat(@NonNull String... groups); + void enableProtoLogToLogcat(@NonNull PrintWriter pw, @NonNull String... groups); /** * Disable logging target groups to logcat. * @param groups we want to disable from being logged to logcat. */ - void disableProtoLogToLogcat(@NonNull String... groups); + void disableProtoLogToLogcat(@NonNull PrintWriter pw, @NonNull String... groups); } diff --git a/core/java/com/android/internal/protolog/ProtoLogConfigurationServiceImpl.java b/core/java/com/android/internal/protolog/ProtoLogConfigurationServiceImpl.java index f83359dddfcc..ac1022ff1e0f 100644 --- a/core/java/com/android/internal/protolog/ProtoLogConfigurationServiceImpl.java +++ b/core/java/com/android/internal/protolog/ProtoLogConfigurationServiceImpl.java @@ -44,6 +44,7 @@ import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.PrintWriter; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -183,8 +184,8 @@ public class ProtoLogConfigurationServiceImpl extends IProtoLogConfigurationServ * @param groups we want to enable logging them to logcat for. */ @Override - public void enableProtoLogToLogcat(@NonNull String... groups) { - toggleProtoLogToLogcat(true, groups); + public void enableProtoLogToLogcat(@NonNull PrintWriter pw, @NonNull String... groups) { + toggleProtoLogToLogcat(pw, true, groups); } /** @@ -192,8 +193,8 @@ public class ProtoLogConfigurationServiceImpl extends IProtoLogConfigurationServ * @param groups we want to disable from being logged to logcat. */ @Override - public void disableProtoLogToLogcat(@NonNull String... groups) { - toggleProtoLogToLogcat(false, groups); + public void disableProtoLogToLogcat(@NonNull PrintWriter pw, @NonNull String... groups) { + toggleProtoLogToLogcat(pw, false, groups); } /** @@ -249,7 +250,9 @@ public class ProtoLogConfigurationServiceImpl extends IProtoLogConfigurationServ } } - private void toggleProtoLogToLogcat(boolean enabled, @NonNull String[] groups) { + private void toggleProtoLogToLogcat( + @NonNull PrintWriter pw, boolean enabled, @NonNull String[] groups + ) { final var clientToGroups = new HashMap<IProtoLogClient, Set<String>>(); for (String group : groups) { @@ -257,8 +260,10 @@ public class ProtoLogConfigurationServiceImpl extends IProtoLogConfigurationServ if (clients == null) { // No clients associated to this group - Log.w(LOG_TAG, "Attempting to toggle log to logcat for group " + group - + " with no registered clients."); + var warning = "Attempting to toggle log to logcat for group " + group + + " with no registered clients. This is a no-op."; + Log.w(LOG_TAG, warning); + pw.println("WARNING: " + warning); continue; } @@ -270,8 +275,14 @@ public class ProtoLogConfigurationServiceImpl extends IProtoLogConfigurationServ for (IProtoLogClient client : clientToGroups.keySet()) { try { - client.toggleLogcat(enabled, clientToGroups.get(client).toArray(new String[0])); + final var clientGroups = clientToGroups.get(client).toArray(new String[0]); + pw.println("Toggling logcat logging for client " + client.toString() + + " to " + enabled + " for groups: [" + + String.join(", ", clientGroups) + "]"); + client.toggleLogcat(enabled, clientGroups); + pw.println("- Done"); } catch (RemoteException e) { + pw.println("- Failed"); throw new RuntimeException( "Failed to toggle logcat status for groups on client", e); } diff --git a/core/jni/android_os_PerfettoTrackEventExtra.cpp b/core/jni/android_os_PerfettoTrackEventExtra.cpp index b8bdc8c29199..18f05cabd12d 100644 --- a/core/jni/android_os_PerfettoTrackEventExtra.cpp +++ b/core/jni/android_os_PerfettoTrackEventExtra.cpp @@ -23,7 +23,8 @@ #include <nativehelper/utils.h> #include <tracing_sdk.h> -static constexpr ssize_t kMaxStrLen = 4096; +#include <list> + namespace android { template <typename T> inline static T* toPointer(jlong ptr) { @@ -35,24 +36,145 @@ inline static jlong toJLong(T* ptr) { return static_cast<jlong>(reinterpret_cast<uintptr_t>(ptr)); } +/** + * @brief A thread-safe utility class for converting Java UTF-16 strings to ASCII in JNI + * environment. + * + * StringBuffer provides efficient conversion of Java strings to ASCII with optimized memory + * handling. + * It uses a two-tiered buffering strategy: + * 1. A fast path using pre-allocated thread-local buffers for strings up to 128 characters + * 2. A fallback path using dynamic allocation for longer strings + * + * Non-ASCII characters (>255) are replaced with '?' during conversion. The class maintains + * thread safety through thread-local storage and provides zero-copy string views for optimal + * performance. + * + * Memory Management: + * - Uses fixed-size thread-local buffers for both UTF-16 and ASCII characters + * - Overflow strings are stored in a thread-local list to maintain valid string views + * - Avoids unnecessary allocations in the common case of small strings + * + * Usage example: + * @code + * JNIEnv* env = ...; + * jstring java_string = ...; + * std::string_view ascii = StringBuffer::utf16_to_ascii(env, java_string); + * // Use the ASCII string... + * StringBuffer::reset(); // Clean up when done + * @endcode + * + * Thread Safety: All methods are thread-safe due to thread-local storage. + */ +class StringBuffer { +private: + static constexpr size_t BASE_SIZE = 128; + // Temporarily stores the UTF-16 characters retrieved from the Java + // string before they are converted to ASCII. + static thread_local inline char char_buffer[BASE_SIZE]; + // For fast-path conversions when the resulting ASCII string fits within + // the pre-allocated space. All ascii strings in a trace event will be stored + // here until emitted. + static thread_local inline jchar jchar_buffer[BASE_SIZE]; + // When the fast-path conversion is not possible (because char_buffer + // doesn't have enough space), the converted ASCII string is stored + // in this list. We use list here to avoid moving the strings on resize + // with vector. This way, we can give out string_views from the stored strings. + // The additional overhead from list node allocations is fine cos we are already + // in an extremely unlikely path here and there are other bigger problems if here. + static thread_local inline std::list<std::string> overflow_strings; + // current offset into the char_buffer. + static thread_local inline size_t current_offset{0}; + // This allows us avoid touching the overflow_strings directly in the fast path. + // Touching it causes some thread local init routine to run which shows up in profiles. + static thread_local inline bool is_overflow_strings_empty = true; + + static void copy_utf16_to_ascii(const jchar* src, size_t len, char* dst, JNIEnv* env, + jstring str) { + std::transform(src, src + len, dst, + [](jchar c) { return (c <= 0xFF) ? static_cast<char>(c) : '?'; }); + + if (src != jchar_buffer) { + // We hit the slow path to populate src, so we have to release. + env->ReleaseStringCritical(str, src); + } + } + +public: + static void reset() { + if (!is_overflow_strings_empty) { + overflow_strings.clear(); + is_overflow_strings_empty = true; + } + current_offset = 0; + } + + // Converts a Java string (jstring) to an ASCII string_view. Characters + // outside the ASCII range (0-255) are replaced with '?'. + // + // @param env The JNI environment. + // @param val The Java string to convert. + // @return A string_view representing the ASCII version of the string. + // Returns an empty string_view if the input is null or empty. + static std::string_view utf16_to_ascii(JNIEnv* env, jstring val) { + if (!val) return ""; + + const jsize len = env->GetStringLength(val); + if (len == 0) return ""; + + const jchar* temp_buffer; + + // Fast path: Enough space in jchar_buffer + if (static_cast<size_t>(len) <= BASE_SIZE) { + env->GetStringRegion(val, 0, len, jchar_buffer); + temp_buffer = jchar_buffer; + } else { + // Slow path: Fallback to asking ART for the string which will likely + // allocate and return a copy. + temp_buffer = env->GetStringCritical(val, nullptr); + } + + const size_t next_offset = current_offset + len + 1; + // Fast path: Enough space in char_buffer + if (BASE_SIZE > next_offset) { + const size_t start_offset = current_offset; + + copy_utf16_to_ascii(temp_buffer, len, char_buffer + current_offset, env, val); + char_buffer[current_offset + len] = '\0'; + + auto res = std::string_view(char_buffer + current_offset, len); + current_offset = next_offset; + return res; + } else { + // Slow path: Not enough space in char_buffer. Use overflow_strings. + // This will cause a string alloc but should be very unlikely to hit. + std::string& str = overflow_strings.emplace_back(len + 1, '\0'); + + copy_utf16_to_ascii(temp_buffer, len, str.data(), env, val); + is_overflow_strings_empty = false; + return std::string_view(str); + } + } +}; + static jlong android_os_PerfettoTrackEventExtraArgInt64_init(JNIEnv* env, jclass, jstring name) { - ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name); - return toJLong(new tracing_perfetto::DebugArg<int64_t>(name_chars.c_str())); + return toJLong(new tracing_perfetto::DebugArg<int64_t>( + StringBuffer::utf16_to_ascii(env, name).data())); } static jlong android_os_PerfettoTrackEventExtraArgBool_init(JNIEnv* env, jclass, jstring name) { - ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name); - return toJLong(new tracing_perfetto::DebugArg<bool>(name_chars.c_str())); + return toJLong( + new tracing_perfetto::DebugArg<bool>(StringBuffer::utf16_to_ascii(env, name).data())); } static jlong android_os_PerfettoTrackEventExtraArgDouble_init(JNIEnv* env, jclass, jstring name) { - ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name); - return toJLong(new tracing_perfetto::DebugArg<double>(name_chars.c_str())); + return toJLong( + new tracing_perfetto::DebugArg<double>(StringBuffer::utf16_to_ascii(env, name).data())); } static jlong android_os_PerfettoTrackEventExtraArgString_init(JNIEnv* env, jclass, jstring name) { - ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name); - return toJLong(new tracing_perfetto::DebugArg<const char*>(name_chars.c_str())); + return toJLong(new tracing_perfetto::DebugArg<const char*>( + StringBuffer::utf16_to_ascii(env, name).data())); } static jlong android_os_PerfettoTrackEventExtraArgInt64_delete() { @@ -109,11 +231,9 @@ static void android_os_PerfettoTrackEventExtraArgDouble_set_value(jlong ptr, jdo static void android_os_PerfettoTrackEventExtraArgString_set_value(JNIEnv* env, jclass, jlong ptr, jstring val) { - ScopedUtfChars val_chars = GET_UTF_OR_RETURN_VOID(env, val); - tracing_perfetto::DebugArg<const char*>* arg = toPointer<tracing_perfetto::DebugArg<const char*>>(ptr); - arg->set_value(strdup(val_chars.c_str())); + arg->set_value(StringBuffer::utf16_to_ascii(env, val).data()); } static jlong android_os_PerfettoTrackEventExtraFieldInt64_init() { @@ -186,11 +306,9 @@ static void android_os_PerfettoTrackEventExtraFieldDouble_set_value(jlong ptr, j static void android_os_PerfettoTrackEventExtraFieldString_set_value(JNIEnv* env, jclass, jlong ptr, jlong id, jstring val) { - ScopedUtfChars val_chars = GET_UTF_OR_RETURN_VOID(env, val); - tracing_perfetto::ProtoField<const char*>* field = toPointer<tracing_perfetto::ProtoField<const char*>>(ptr); - field->set_value(id, strdup(val_chars.c_str())); + field->set_value(id, StringBuffer::utf16_to_ascii(env, val).data()); } static void android_os_PerfettoTrackEventExtraFieldNested_add_field(jlong field_ptr, @@ -231,8 +349,9 @@ static jlong android_os_PerfettoTrackEventExtraFlow_get_extra_ptr(jlong ptr) { static jlong android_os_PerfettoTrackEventExtraNamedTrack_init(JNIEnv* env, jclass, jlong id, jstring name, jlong parent_uuid) { - ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name); - return toJLong(new tracing_perfetto::NamedTrack(id, parent_uuid, name_chars.c_str())); + return toJLong( + new tracing_perfetto::NamedTrack(id, parent_uuid, + StringBuffer::utf16_to_ascii(env, name).data())); } static jlong android_os_PerfettoTrackEventExtraNamedTrack_delete() { @@ -246,9 +365,10 @@ static jlong android_os_PerfettoTrackEventExtraNamedTrack_get_extra_ptr(jlong pt static jlong android_os_PerfettoTrackEventExtraCounterTrack_init(JNIEnv* env, jclass, jstring name, jlong parent_uuid) { - ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name); - - return toJLong(new tracing_perfetto::RegisteredTrack(1, parent_uuid, name_chars.c_str(), true)); + return toJLong( + new tracing_perfetto::RegisteredTrack(1, parent_uuid, + StringBuffer::utf16_to_ascii(env, name).data(), + true)); } static jlong android_os_PerfettoTrackEventExtraCounterTrack_delete() { @@ -318,11 +438,11 @@ static void android_os_PerfettoTrackEventExtra_clear_args(jlong ptr) { static void android_os_PerfettoTrackEventExtra_emit(JNIEnv* env, jclass, jint type, jlong cat_ptr, jstring name, jlong extra_ptr) { - ScopedUtfChars name_chars = GET_UTF_OR_RETURN_VOID(env, name); - tracing_perfetto::Category* category = toPointer<tracing_perfetto::Category>(cat_ptr); - tracing_perfetto::trace_event(type, category->get(), name_chars.c_str(), + tracing_perfetto::trace_event(type, category->get(), + StringBuffer::utf16_to_ascii(env, name).data(), toPointer<tracing_perfetto::Extra>(extra_ptr)); + StringBuffer::reset(); } static jlong android_os_PerfettoTrackEventExtraProto_init() { diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index b2649a471b8a..a73ff421acf7 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -424,6 +424,15 @@ static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jin assetmanager->SetDefaultLocale(default_locale_int); } +static void NativeSetOverlayConstraints(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, + jint displayId, jint deviceId) { + ATRACE_NAME("AssetManager::SetDisplayIdAndDeviceId"); + + auto assetmanager = LockAndStartAssetManager(ptr); + assetmanager->SetOverlayConstraints(static_cast<int32_t>(displayId), + static_cast<int32_t>(deviceId)); +} + static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr, jboolean includeOverlays, jboolean includeLoaders) { @@ -1554,6 +1563,7 @@ static const JNINativeMethod gAssetManagerMethods[] = { {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;ZZ)V", (void*)NativeSetApkAssets}, {"nativeSetConfiguration", "(JIILjava/lang/String;[Ljava/lang/String;IIIIIIIIIIIIIIIIZ)V", (void*)NativeSetConfiguration}, + {"nativeSetOverlayConstraints", "(JII)V", (void*)NativeSetOverlayConstraints}, {"nativeGetAssignedPackageIdentifiers", "(JZZ)Landroid/util/SparseArray;", (void*)NativeGetAssignedPackageIdentifiers}, diff --git a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp index 7ffe0ed7c6cd..8f36ecb8b01a 100644 --- a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp +++ b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp @@ -97,7 +97,13 @@ static void native_updateValues(JNIEnv *env, jclass, jlong nativePtr, jlongArray static void native_incrementValues(JNIEnv *env, jclass, jlong nativePtr, jlongArray values, jlong timestamp) { auto *counter = reinterpret_cast<LongArrayMultiStateCounter *>(nativePtr); - counter->incrementValue(JavaUint64Array(env, values), timestamp); + if (values != nullptr) { + counter->incrementValue(JavaUint64Array(env, values), timestamp); + } else { + // Pass an empty Uint64Array, which is equivalent to an array of zeros. + // This is done to ensure that the timestamp is still updated in the counter. + counter->incrementValue(Uint64Array(), timestamp); + } } static void native_addCounts(JNIEnv *env, jclass, jlong nativePtr, jlongArray values) { diff --git a/core/proto/OWNERS b/core/proto/OWNERS index b51f72dee260..aa8f8419ac46 100644 --- a/core/proto/OWNERS +++ b/core/proto/OWNERS @@ -5,7 +5,6 @@ joeo@google.com singhtejinder@google.com yanmin@google.com yaochen@google.com -yro@google.com zhouwenjie@google.com # Frameworks diff --git a/core/res/res/drawable-w192dp/loader_horizontal_watch.xml b/core/res/res/drawable-w192dp/loader_horizontal_watch.xml deleted file mode 100644 index 18cea6e0d87d..000000000000 --- a/core/res/res/drawable-w192dp/loader_horizontal_watch.xml +++ /dev/null @@ -1,97 +0,0 @@ -<!-- - ~ Copyright (C) 2025 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="15dp" android:width="67dp" android:viewportHeight="15" android:viewportWidth="67"> - <group android:name="_R_G"> - <group android:name="_R_G_L_1_G" android:translateX="33.5" android:translateY="7.5"> - <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M33.5 -7.5 C33.5,-7.5 33.5,7.5 33.5,7.5 C33.5,7.5 -33.5,7.5 -33.5,7.5 C-33.5,7.5 -33.5,-7.5 -33.5,-7.5 C-33.5,-7.5 33.5,-7.5 33.5,-7.5c "/> - </group> - <group android:name="_R_G_L_0_G" android:translateX="-296.5" android:translateY="-62.5" android:pivotX="330" android:pivotY="70" android:scaleX="0.1" android:scaleY="0.1"> - <group android:name="_R_G_L_0_G_L_6_G" android:translateX="-224.84700000000004" android:translateY="-321.948" android:pivotX="555.09" android:pivotY="-329" android:rotation="28.9" android:scaleY="0"> - <path android:name="_R_G_L_0_G_L_6_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M194.88 359 C190,359 185.05,357.81 180.48,355.3 C59.86,289.14 -41.55,191.9 -112.79,74.11 C-186.14,-47.16 -224.91,-186.55 -224.91,-329 C-224.91,-345.57 -211.48,-359 -194.91,-359 C-178.34,-359 -164.91,-345.57 -164.91,-329 C-164.91,-197.5 -129.13,-68.84 -61.45,43.06 C4.33,151.82 97.97,241.6 209.33,302.69 C223.86,310.66 229.18,328.9 221.21,343.42 C215.75,353.37 205.48,359 194.88,359c "/> - </group> - <group android:name="_R_G_L_0_G_L_5_G" android:translateX="744.323" android:translateY="-277.96299999999997" android:pivotX="-414.08" android:pivotY="-372.985" android:rotation="28.9"> - <path android:name="_R_G_L_0_G_L_5_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-335.95 402.99 C-351.13,402.99 -364.16,391.5 -365.76,376.07 C-367.46,359.59 -355.49,344.85 -339.01,343.14 C-162.93,324.91 -0.15,242.33 119.34,110.62 C239.66,-22.01 305.92,-193.76 305.92,-372.98 C305.92,-389.55 319.35,-402.98 335.92,-402.98 C352.49,-402.98 365.92,-389.55 365.92,-372.98 C365.92,-178.82 294.13,7.24 163.78,150.93 C34.34,293.61 -142.03,383.07 -332.83,402.82 C-333.88,402.93 -334.92,402.99 -335.95,402.99c "/> - </group> - <group android:name="_R_G_L_0_G_L_2_G" android:translateX="185.385" android:translateY="70.09100000000001" android:pivotX="144.858" android:pivotY="-721.039" android:rotation="28.9" android:scaleY="0"> - <path android:name="_R_G_L_0_G_L_2_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M144.62 58.96 C144.61,58.96 144.61,58.96 144.6,58.96 C40.39,58.93 -60.82,38.66 -156.19,-1.28 C-171.48,-7.68 -178.68,-25.26 -172.28,-40.54 C-165.88,-55.82 -148.3,-63.02 -133.02,-56.62 C-45.02,-19.77 48.4,-1.07 144.63,-1.04 C161.19,-1.03 174.62,12.4 174.62,28.97 C174.61,45.53 161.18,58.96 144.62,58.96c "/> - </group> - <group android:name="_R_G_L_0_G_L_0_G" android:translateX="330" android:translateY="70"> - <path android:name="_R_G_L_0_G_L_0_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-660 -313 C-660,-313 -660,313 -660,313 C-660,313 660,313 660,313 C660,313 660,-313 660,-313 C660,-313 -660,-313 -660,-313c M300.74 -1.16 C205.46,38.62 103.22,59.09 -0.03,59.05 C-103.28,59.01 -205.51,38.48 -300.76,-1.37 C-316.05,-7.76 -323.26,-25.34 -316.86,-40.62 C-310.47,-55.91 -292.9,-63.12 -277.61,-56.72 C-189.68,-19.94 -95.32,-0.98 -0.01,-0.95 C95.3,-0.92 189.67,-19.81 277.63,-56.53 C292.92,-62.91 310.49,-55.69 316.87,-40.4 C323.25,-25.11 316.03,-7.54 300.74,-1.16c "/> - </group> - </group> - </group> - <group android:name="time_group"/> - </vector> - </aapt:attr> - <target android:name="_R_G_L_0_G_L_6_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.9" android:valueTo="-51.4" android:valueType="floatType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.135 0.202,0.848 1.0,1.0"/> - </aapt:attr> - </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_5_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.9" android:valueTo="-51.4" android:valueType="floatType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.135 0.202,0.848 1.0,1.0"/> - </aapt:attr> - </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_5_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_2_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.9" android:valueTo="-51.4" android:valueType="floatType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.135 0.202,0.848 1.0,1.0"/> - </aapt:attr> - </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_2_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="133" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/> - </set> - </aapt:attr> - </target> - <target android:name="time_group"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="translateX" android:duration="1000" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/> - </set> - </aapt:attr> - </target> -</animated-vector> - diff --git a/core/res/res/drawable-w204dp/loader_horizontal_watch.xml b/core/res/res/drawable-w204dp/loader_horizontal_watch.xml deleted file mode 100644 index fbc6eab320eb..000000000000 --- a/core/res/res/drawable-w204dp/loader_horizontal_watch.xml +++ /dev/null @@ -1,104 +0,0 @@ -<!-- - ~ Copyright (C) 2025 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="15dp" android:width="70dp" android:viewportHeight="15" android:viewportWidth="70"> - <group android:name="_R_G"> - <group android:name="_R_G_L_1_G" android:translateX="35" android:translateY="7.5"> - <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M35 -7.5 C35,-7.5 35,7.5 35,7.5 C35,7.5 -35,7.5 -35,7.5 C-35,7.5 -35,-7.5 -35,-7.5 C-35,-7.5 35,-7.5 35,-7.5c "/> - </group> - <group android:name="_R_G_L_0_G" android:translateX="-310" android:translateY="-64" android:pivotX="345" android:pivotY="71.5" android:scaleX="0.1" android:scaleY="0.1"> - <group android:name="_R_G_L_0_G_L_6_G" android:translateX="-239.44799999999998" android:translateY="-341.45" android:pivotX="584.448" android:pivotY="-346.55" android:rotation="28.8" android:scaleY="0"> - <path android:name="_R_G_L_0_G_L_6_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M205.28 376.55 C200.4,376.55 195.46,375.36 190.88,372.85 C64.08,303.29 -42.54,201.07 -117.44,77.24 C-194.55,-50.25 -235.31,-196.79 -235.31,-346.55 C-235.31,-363.12 -221.88,-376.55 -205.31,-376.55 C-188.74,-376.55 -175.31,-363.12 -175.31,-346.55 C-175.31,-207.74 -137.54,-71.93 -66.1,46.19 C3.34,160.99 102.18,255.76 219.73,320.24 C234.26,328.21 239.58,346.45 231.61,360.97 C226.15,370.92 215.88,376.55 205.28,376.55c "/> - </group> - <group android:name="_R_G_L_0_G_L_5_G" android:translateX="781.413" android:translateY="-295.124" android:pivotX="-436.413" android:pivotY="-392.876" android:rotation="28.8"> - <path android:name="_R_G_L_0_G_L_5_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-353.86 422.88 C-369.04,422.88 -382.07,411.4 -383.67,395.97 C-385.37,379.49 -373.4,364.74 -356.92,363.03 C-171.06,343.79 0.76,256.62 126.89,117.59 C253.89,-22.41 323.83,-203.7 323.83,-392.88 C323.83,-409.44 337.26,-422.88 353.83,-422.88 C370.4,-422.88 383.83,-409.44 383.83,-392.88 C383.83,-188.76 308.36,6.84 171.32,157.9 C35.25,307.89 -150.15,401.94 -350.74,422.72 C-351.79,422.82 -352.83,422.88 -353.86,422.88c "/> - </group> - <group android:name="_R_G_L_0_G_L_2_G" android:translateX="192.671" android:translateY="71.49599999999998" android:pivotX="152.329" android:pivotY="-759.496" android:rotation="28.8" android:scaleY="0"> - <path android:name="_R_G_L_0_G_L_2_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M152.33 60.5 C152.33,60.5 152.32,60.5 152.32,60.5 C42.76,60.47 -63.64,39.16 -163.91,-2.82 C-179.19,-9.22 -186.39,-26.8 -179.99,-42.08 C-173.59,-57.36 -156.02,-64.57 -140.73,-58.16 C-47.84,-19.27 50.77,0.47 152.34,0.5 C168.91,0.51 182.33,13.94 182.33,30.51 C182.32,47.08 168.89,60.5 152.33,60.5c "/> - </group> - <group android:name="_R_G_L_0_G_L_0_G" android:translateX="345" android:translateY="71.5"> - <path android:name="_R_G_L_0_G_L_0_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-579 -259.5 C-579,-259.5 -579,259.5 -579,259.5 C-579,259.5 579,259.5 579,259.5 C579,259.5 579,-259.5 579,-259.5 C579,-259.5 -579,-259.5 -579,-259.5c M316.17 -2.8 C216,39.02 108.52,60.54 -0.03,60.5 C-108.58,60.46 -216.04,38.87 -316.18,-3.02 C-331.47,-9.41 -338.68,-26.99 -332.28,-42.27 C-325.89,-57.56 -308.32,-64.76 -293.03,-58.37 C-200.22,-19.55 -100.61,0.46 -0.01,0.5 C100.6,0.54 200.22,-19.41 293.06,-58.17 C308.35,-64.55 325.92,-57.33 332.3,-42.04 C338.68,-26.75 331.46,-9.18 316.17,-2.8c "/> - </group> - </group> - </group> - <group android:name="time_group"/> - </vector> - </aapt:attr> - <target android:name="_R_G_L_0_G_L_6_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.8" android:valueTo="-51.4" android:valueType="floatType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/> - </aapt:attr> - </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_6_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="333" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_5_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.8" android:valueTo="-51.4" android:valueType="floatType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/> - </aapt:attr> - </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_5_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_2_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.8" android:valueTo="-51.4" android:valueType="floatType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/> - </aapt:attr> - </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_2_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="133" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/> - </set> - </aapt:attr> - </target> - <target android:name="time_group"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="translateX" android:duration="1000" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/> - </set> - </aapt:attr> - </target> -</animated-vector> - diff --git a/core/res/res/drawable-w216dp/loader_horizontal_watch.xml b/core/res/res/drawable-w216dp/loader_horizontal_watch.xml deleted file mode 100644 index ed4b7ea0ff02..000000000000 --- a/core/res/res/drawable-w216dp/loader_horizontal_watch.xml +++ /dev/null @@ -1,105 +0,0 @@ -<!-- - ~ Copyright (C) 2025 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="74dp" android:viewportHeight="16" android:viewportWidth="74"> - <group android:name="_R_G"> - <group android:name="_R_G_L_1_G" android:translateX="37" android:translateY="8"> - <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M37 -8 C37,-8 37,8 37,8 C37,8 -37,8 -37,8 C-37,8 -37,-8 -37,-8 C-37,-8 37,-8 37,-8c "/> - </group> - <group android:name="_R_G_L_0_G" android:translateX="-328" android:translateY="-65.5" android:pivotX="365" android:pivotY="73.5" android:scaleX="0.1" android:scaleY="0.1"> - <group android:name="_R_G_L_0_G_L_6_G" android:translateX="-256.447" android:translateY="-365.014" android:pivotX="621.447" android:pivotY="-368.486" android:rotation="28.8" android:scaleY="0"> - <path android:name="_R_G_L_0_G_L_6_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M218.28 398.49 C213.4,398.49 208.46,397.3 203.88,394.78 C69.34,320.99 -43.78,212.53 -123.25,81.15 C-205.06,-54.11 -248.31,-209.59 -248.31,-368.49 C-248.31,-385.05 -234.88,-398.49 -218.31,-398.49 C-201.74,-398.49 -188.31,-385.05 -188.31,-368.49 C-188.31,-220.54 -148.06,-75.8 -71.91,50.09 C2.1,172.45 107.45,273.45 232.73,342.18 C247.26,350.15 252.58,368.38 244.61,382.91 C239.15,392.86 228.88,398.49 218.28,398.49c "/> - </group> - <group android:name="_R_G_L_0_G_L_5_G" android:translateX="829.0260000000001" android:translateY="-315.759" android:pivotX="-464.026" android:pivotY="-417.741" android:rotation="28.8"> - <path android:name="_R_G_L_0_G_L_5_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-376.25 447.74 C-391.43,447.74 -404.46,436.26 -406.05,420.83 C-407.76,404.35 -395.78,389.61 -379.3,387.9 C-181.22,367.38 1.9,274.48 136.32,126.3 C271.67,-22.9 346.22,-216.12 346.22,-417.74 C346.22,-434.31 359.65,-447.74 376.22,-447.74 C392.79,-447.74 406.22,-434.31 406.22,-417.74 C406.22,-201.18 326.15,6.35 180.76,166.61 C36.39,325.75 -160.31,425.54 -373.12,447.58 C-374.17,447.69 -375.22,447.74 -376.25,447.74c "/> - </group> - <group android:name="_R_G_L_0_G_L_2_G" android:translateX="203.029" android:translateY="74.06899999999996" android:pivotX="161.971" android:pivotY="-807.569" android:rotation="28.8" android:scaleY="0"> - <path android:name="_R_G_L_0_G_L_2_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M161.97 62.43 C161.97,62.43 161.96,62.43 161.96,62.43 C45.71,62.4 -67.17,39.79 -173.55,-4.75 C-188.83,-11.15 -196.03,-28.72 -189.63,-44.01 C-183.24,-59.29 -165.66,-66.49 -150.38,-60.09 C-51.37,-18.64 53.72,2.4 161.98,2.43 C178.55,2.44 191.98,15.87 191.97,32.44 C191.97,49 178.54,62.43 161.97,62.43c "/> - </group> - <group android:name="_R_G_L_0_G_L_0_G" android:translateX="365" android:translateY="73.5"> - <path android:name="_R_G_L_0_G_L_0_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-609 -244.5 C-609,-244.5 -609,244.5 -609,244.5 C-609,244.5 609,244.5 609,244.5 C609,244.5 609,-244.5 609,-244.5 C609,-244.5 -609,-244.5 -609,-244.5c M335.44 -4.16 C229.17,40.21 115.13,63.04 -0.04,63 C-115.21,62.96 -229.22,40.05 -335.47,-4.39 C-350.76,-10.79 -357.95,-28.36 -351.56,-43.65 C-345.17,-58.93 -327.59,-66.14 -312.31,-59.74 C-213.39,-18.36 -107.24,2.96 -0.02,3 C107.21,3.04 213.38,-18.22 312.33,-59.53 C327.62,-65.91 345.19,-58.69 351.57,-43.4 C357.95,-28.11 350.73,-10.54 335.44,-4.16c "/> - </group> - </group> - </group> - <group android:name="time_group"/> - </vector> - </aapt:attr> - <target android:name="_R_G_L_0_G_L_6_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.8" android:valueTo="-51.3" android:valueType="floatType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/> - </aapt:attr> - </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_6_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="333" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_5_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.8" android:valueTo="-51.3" android:valueType="floatType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/> - </aapt:attr> - </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_5_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_2_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.8" android:valueTo="-51.3" android:valueType="floatType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/> - </aapt:attr> - </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_2_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="133" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/> - </set> - </aapt:attr> - </target> - <target android:name="time_group"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="translateX" android:duration="1000" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/> - </set> - </aapt:attr> - </target> -</animated-vector> - - diff --git a/core/res/res/drawable-w228dp/loader_horizontal_watch.xml b/core/res/res/drawable-w228dp/loader_horizontal_watch.xml deleted file mode 100644 index 6b86c634d554..000000000000 --- a/core/res/res/drawable-w228dp/loader_horizontal_watch.xml +++ /dev/null @@ -1,103 +0,0 @@ -<!-- - ~ Copyright (C) 2025 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="14dp" android:width="76dp" android:viewportHeight="14" android:viewportWidth="76"> - <group android:name="_R_G"> - <group android:name="_R_G_L_1_G" android:translateX="39" android:translateY="8"> - <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M39 -8 C39,-8 39,8 39,8 C39,8 -39,8 -39,8 C-39,8 -39,-8 -39,-8 C-39,-8 39,-8 39,-8c "/> - </group> - <group android:name="_R_G_L_0_G" android:translateX="-345" android:translateY="-67" android:pivotX="384" android:pivotY="75" android:scaleX="0.1" android:scaleY="0.1"> - <group android:name="_R_G_L_0_G_L_6_G" android:translateX="-274.19" android:translateY="-390.077" android:pivotX="658.448" android:pivotY="-390.423" android:rotation="28.7" android:scaleY="0"> - <path android:name="_R_G_L_0_G_L_6_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M231.28 420.42 C226.4,420.42 221.45,419.23 216.88,416.72 C74.61,338.68 -45.02,224 -129.06,85.06 C-171.54,14.82 -204.38,-60.73 -226.66,-139.5 C-249.65,-220.76 -261.31,-305.18 -261.31,-390.42 C-261.31,-406.99 -247.88,-420.42 -231.31,-420.42 C-214.74,-420.42 -201.31,-406.99 -201.31,-390.42 C-201.31,-310.71 -190.42,-231.78 -168.93,-155.83 C-148.11,-82.23 -117.42,-11.63 -77.72,54 C0.86,183.92 112.71,291.15 245.73,364.12 C260.26,372.08 265.58,390.32 257.61,404.85 C252.15,414.79 241.88,420.42 231.28,420.42c "/> - </group> - <group android:name="_R_G_L_0_G_L_5_G" android:translateX="875.8979999999999" android:translateY="-337.894" android:pivotX="-491.64" android:pivotY="-442.606" android:rotation="28.7"> - <path android:name="_R_G_L_0_G_L_5_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-398.64 472.61 C-413.82,472.61 -426.84,461.13 -428.44,445.7 C-430.15,429.22 -418.17,414.47 -401.69,412.77 C-191.38,390.98 3.04,292.33 145.75,135.01 C289.46,-23.4 368.6,-228.54 368.6,-442.61 C368.6,-459.17 382.04,-472.61 398.6,-472.61 C415.17,-472.61 428.6,-459.17 428.6,-442.61 C428.6,-213.6 343.93,5.85 190.19,175.33 C37.53,343.61 -170.48,449.13 -395.51,472.44 C-396.56,472.55 -397.6,472.61 -398.64,472.61c "/> - </group> - <group android:name="_R_G_L_0_G_L_2_G" android:translateX="212.64499999999998" android:translateY="75.14200000000005" android:pivotX="171.613" android:pivotY="-855.642" android:rotation="28.7" android:scaleY="0"> - <path android:name="_R_G_L_0_G_L_2_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M171.61 64.36 C171.61,64.36 171.61,64.36 171.61,64.36 C48.68,64.32 -70.7,40.42 -183.19,-6.68 C-198.47,-13.07 -205.68,-30.65 -199.28,-45.93 C-192.88,-61.22 -175.3,-68.42 -160.02,-62.02 C-54.9,-18.01 56.68,4.33 171.62,4.36 C188.19,4.36 201.62,17.8 201.61,34.36 C201.61,50.93 188.18,64.36 171.61,64.36c "/> - </group> - <group android:name="_R_G_L_0_G_L_0_G" android:translateX="384" android:translateY="75"> - <path android:name="_R_G_L_0_G_L_0_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-611 -259 C-611,-259 -611,259 -611,259 C-611,259 611,259 611,259 C611,259 611,-259 611,-259 C611,-259 -611,-259 -611,-259c M354.66 -6.52 C242.36,40.4 121.76,64.54 -0.04,64.5 C-121.84,64.46 -242.44,40.23 -354.74,-6.76 C-370.04,-13.16 -377.24,-30.73 -370.84,-46.02 C-364.44,-61.3 -346.94,-68.51 -331.64,-62.12 C-226.54,-18.18 -113.84,4.46 -0.04,4.5 C113.76,4.54 226.56,-18.02 331.56,-61.89 C346.86,-68.27 364.46,-61.05 370.86,-45.76 C377.26,-30.47 369.96,-12.9 354.66,-6.52c "/> - </group> - </group> - </group> - <group android:name="time_group"/> - </vector> - </aapt:attr> - <target android:name="_R_G_L_0_G_L_6_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/> - </aapt:attr> - </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_6_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="333" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_5_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/> - </aapt:attr> - </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_5_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_2_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/> - </aapt:attr> - </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_2_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="133" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/> - </set> - </aapt:attr> - </target> - <target android:name="time_group"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="translateX" android:duration="1000" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/> - </set> - </aapt:attr> - </target> -</animated-vector> diff --git a/core/res/res/drawable-w240dp/loader_horizontal_watch.xml b/core/res/res/drawable-w240dp/loader_horizontal_watch.xml deleted file mode 100644 index ad60bbdc420c..000000000000 --- a/core/res/res/drawable-w240dp/loader_horizontal_watch.xml +++ /dev/null @@ -1,104 +0,0 @@ -<!-- - ~ Copyright (C) 2025 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="17dp" android:width="82dp" android:viewportHeight="17" android:viewportWidth="82"> - <group android:name="_R_G"> - <group android:name="_R_G_L_1_G" android:translateX="41" android:translateY="8.5"> - <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M41 -8.5 C41,-8.5 41,8.5 41,8.5 C41,8.5 -41,8.5 -41,8.5 C-41,8.5 -41,-8.5 -41,-8.5 C-41,-8.5 41,-8.5 41,-8.5c "/> - </group> - <group android:name="_R_G_L_0_G" android:translateX="-362.5" android:translateY="-69" android:pivotX="403.5" android:pivotY="77.5" android:scaleX="0.1" android:scaleY="0.1"> - <group android:name="_R_G_L_0_G_L_6_G" android:translateX="-291.64799999999997" android:translateY="-414.141" android:pivotX="695.448" android:pivotY="-412.359" android:rotation="28.7" android:scaleY="0"> - <path android:name="_R_G_L_0_G_L_6_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M244.28 442.36 C239.4,442.36 234.45,441.17 229.88,438.66 C79.87,356.38 -46.26,235.46 -134.87,88.96 C-179.66,14.91 -214.28,-64.74 -237.78,-147.79 C-262.02,-233.47 -274.31,-322.49 -274.31,-412.36 C-274.31,-428.93 -260.88,-442.36 -244.31,-442.36 C-227.74,-442.36 -214.31,-428.93 -214.31,-412.36 C-214.31,-328.01 -202.78,-244.49 -180.05,-164.12 C-158.01,-86.24 -125.54,-11.54 -83.53,57.91 C-0.38,195.38 117.97,308.85 258.73,386.05 C273.26,394.02 278.58,412.26 270.61,426.78 C265.15,436.73 254.88,442.36 244.28,442.36c "/> - </group> - <group android:name="_R_G_L_0_G_L_5_G" android:translateX="923.0530000000001" android:translateY="-359.029" android:pivotX="-519.253" android:pivotY="-467.471" android:rotation="28.7"> - <path android:name="_R_G_L_0_G_L_5_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-421.02 497.47 C-436.2,497.47 -449.23,485.99 -450.83,470.56 C-452.53,454.08 -440.56,439.34 -424.08,437.63 C-201.54,414.57 4.18,310.19 155.19,143.73 C229.54,61.77 287.7,-31.75 328.04,-134.22 C369.81,-240.3 390.99,-352.42 390.99,-467.47 C390.99,-484.04 404.42,-497.47 420.99,-497.47 C437.56,-497.47 450.99,-484.04 450.99,-467.47 C450.99,-226.02 361.72,5.35 199.63,184.04 C38.67,361.47 -180.63,472.73 -417.89,497.31 C-418.94,497.42 -419.99,497.47 -421.02,497.47c "/> - </group> - <group android:name="_R_G_L_0_G_L_2_G" android:translateX="222.54600000000002" android:translateY="77.21400000000006" android:pivotX="181.254" android:pivotY="-903.714" android:rotation="28.7" android:scaleY="0"> - <path android:name="_R_G_L_0_G_L_2_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M181.26 66.28 C181.25,66.28 181.25,66.28 181.25,66.28 C51.64,66.25 -74.22,41.06 -192.83,-8.6 C-208.12,-15 -215.32,-32.58 -208.92,-47.86 C-202.52,-63.15 -184.94,-70.35 -169.66,-63.95 C-58.42,-17.38 59.64,6.25 181.26,6.28 C197.83,6.29 211.26,19.72 211.26,36.29 C211.25,52.86 197.82,66.28 181.26,66.28c "/> - </group> - <group android:name="_R_G_L_0_G_L_0_G" android:translateX="403.5" android:translateY="77.5"> - <path android:name="_R_G_L_0_G_L_0_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-630.5 -255.5 C-630.5,-255.5 -630.5,255.5 -630.5,255.5 C-630.5,255.5 630.5,255.5 630.5,255.5 C630.5,255.5 630.5,-255.5 630.5,-255.5 C630.5,-255.5 -630.5,-255.5 -630.5,-255.5c M374 -8.88 C255.5,40.59 128.4,66.04 0,66 C-128.4,65.95 -255.6,40.42 -374,-9.14 C-389.3,-15.53 -396.5,-33.11 -390.1,-48.39 C-383.7,-63.68 -366.2,-70.88 -350.9,-64.49 C-239.7,-18 -120.5,5.96 0,6 C120.4,6.04 239.7,-17.84 350.9,-64.25 C366.2,-70.63 383.7,-63.41 390.1,-48.12 C396.5,-32.83 389.3,-15.26 374,-8.88c "/> - </group> - </group> - </group> - <group android:name="time_group"/> - </vector> - </aapt:attr> - <target android:name="_R_G_L_0_G_L_6_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/> - </aapt:attr> - </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_6_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="333" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_5_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/> - </aapt:attr> - </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_5_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_2_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/> - </aapt:attr> - </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_2_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="133" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/> - </set> - </aapt:attr> - </target> - <target android:name="time_group"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="translateX" android:duration="1000" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/> - </set> - </aapt:attr> - </target> -</animated-vector> - diff --git a/core/res/res/drawable/loader_horizontal_watch.xml b/core/res/res/drawable/loader_horizontal_watch.xml deleted file mode 100644 index 6b86c634d554..000000000000 --- a/core/res/res/drawable/loader_horizontal_watch.xml +++ /dev/null @@ -1,103 +0,0 @@ -<!-- - ~ Copyright (C) 2025 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="14dp" android:width="76dp" android:viewportHeight="14" android:viewportWidth="76"> - <group android:name="_R_G"> - <group android:name="_R_G_L_1_G" android:translateX="39" android:translateY="8"> - <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M39 -8 C39,-8 39,8 39,8 C39,8 -39,8 -39,8 C-39,8 -39,-8 -39,-8 C-39,-8 39,-8 39,-8c "/> - </group> - <group android:name="_R_G_L_0_G" android:translateX="-345" android:translateY="-67" android:pivotX="384" android:pivotY="75" android:scaleX="0.1" android:scaleY="0.1"> - <group android:name="_R_G_L_0_G_L_6_G" android:translateX="-274.19" android:translateY="-390.077" android:pivotX="658.448" android:pivotY="-390.423" android:rotation="28.7" android:scaleY="0"> - <path android:name="_R_G_L_0_G_L_6_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M231.28 420.42 C226.4,420.42 221.45,419.23 216.88,416.72 C74.61,338.68 -45.02,224 -129.06,85.06 C-171.54,14.82 -204.38,-60.73 -226.66,-139.5 C-249.65,-220.76 -261.31,-305.18 -261.31,-390.42 C-261.31,-406.99 -247.88,-420.42 -231.31,-420.42 C-214.74,-420.42 -201.31,-406.99 -201.31,-390.42 C-201.31,-310.71 -190.42,-231.78 -168.93,-155.83 C-148.11,-82.23 -117.42,-11.63 -77.72,54 C0.86,183.92 112.71,291.15 245.73,364.12 C260.26,372.08 265.58,390.32 257.61,404.85 C252.15,414.79 241.88,420.42 231.28,420.42c "/> - </group> - <group android:name="_R_G_L_0_G_L_5_G" android:translateX="875.8979999999999" android:translateY="-337.894" android:pivotX="-491.64" android:pivotY="-442.606" android:rotation="28.7"> - <path android:name="_R_G_L_0_G_L_5_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-398.64 472.61 C-413.82,472.61 -426.84,461.13 -428.44,445.7 C-430.15,429.22 -418.17,414.47 -401.69,412.77 C-191.38,390.98 3.04,292.33 145.75,135.01 C289.46,-23.4 368.6,-228.54 368.6,-442.61 C368.6,-459.17 382.04,-472.61 398.6,-472.61 C415.17,-472.61 428.6,-459.17 428.6,-442.61 C428.6,-213.6 343.93,5.85 190.19,175.33 C37.53,343.61 -170.48,449.13 -395.51,472.44 C-396.56,472.55 -397.6,472.61 -398.64,472.61c "/> - </group> - <group android:name="_R_G_L_0_G_L_2_G" android:translateX="212.64499999999998" android:translateY="75.14200000000005" android:pivotX="171.613" android:pivotY="-855.642" android:rotation="28.7" android:scaleY="0"> - <path android:name="_R_G_L_0_G_L_2_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M171.61 64.36 C171.61,64.36 171.61,64.36 171.61,64.36 C48.68,64.32 -70.7,40.42 -183.19,-6.68 C-198.47,-13.07 -205.68,-30.65 -199.28,-45.93 C-192.88,-61.22 -175.3,-68.42 -160.02,-62.02 C-54.9,-18.01 56.68,4.33 171.62,4.36 C188.19,4.36 201.62,17.8 201.61,34.36 C201.61,50.93 188.18,64.36 171.61,64.36c "/> - </group> - <group android:name="_R_G_L_0_G_L_0_G" android:translateX="384" android:translateY="75"> - <path android:name="_R_G_L_0_G_L_0_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-611 -259 C-611,-259 -611,259 -611,259 C-611,259 611,259 611,259 C611,259 611,-259 611,-259 C611,-259 -611,-259 -611,-259c M354.66 -6.52 C242.36,40.4 121.76,64.54 -0.04,64.5 C-121.84,64.46 -242.44,40.23 -354.74,-6.76 C-370.04,-13.16 -377.24,-30.73 -370.84,-46.02 C-364.44,-61.3 -346.94,-68.51 -331.64,-62.12 C-226.54,-18.18 -113.84,4.46 -0.04,4.5 C113.76,4.54 226.56,-18.02 331.56,-61.89 C346.86,-68.27 364.46,-61.05 370.86,-45.76 C377.26,-30.47 369.96,-12.9 354.66,-6.52c "/> - </group> - </group> - </group> - <group android:name="time_group"/> - </vector> - </aapt:attr> - <target android:name="_R_G_L_0_G_L_6_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/> - </aapt:attr> - </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_6_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="333" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_5_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/> - </aapt:attr> - </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_5_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_2_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType"> - <aapt:attr name="android:interpolator"> - <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/> - </aapt:attr> - </objectAnimator> - </set> - </aapt:attr> - </target> - <target android:name="_R_G_L_0_G_L_2_G"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="133" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/> - </set> - </aapt:attr> - </target> - <target android:name="time_group"> - <aapt:attr name="android:animation"> - <set android:ordering="together"> - <objectAnimator android:repeatCount="infinite" android:propertyName="translateX" android:duration="1000" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/> - </set> - </aapt:attr> - </target> -</animated-vector> diff --git a/core/res/res/drawable/progress_ring_watch.xml b/core/res/res/drawable/progress_ring_watch.xml index 8250ee600a8f..2e65ff13b3de 100644 --- a/core/res/res/drawable/progress_ring_watch.xml +++ b/core/res/res/drawable/progress_ring_watch.xml @@ -16,27 +16,27 @@ --> <rotate xmlns:android="http://schemas.android.com/apk/res/android" - android:fromDegrees = "270" - android:toDegrees="270" - android:pivotX="50%" - android:pivotY="50%" > + android:fromDegrees="270" + android:toDegrees="270"> <layer-list> <item> <shape - android:shape="ring" - android:innerRadiusRatio="@dimen/progressbar_inner_radius_ratio" + android:shape="arc" + android:useLevel="false" android:thickness="@dimen/progressbar_thickness" - android:useLevel="false"> - <solid android:color="@color/materialColorSurfaceContainer"/> + android:innerRadiusRatio="@dimen/progressbar_inner_radius_ratio" + android:strokeCap="round"> + <solid android:color="@*android:color/materialColorSurfaceContainer"/> </shape> </item> <item> <shape - android:shape="ring" - android:innerRadiusRatio="@dimen/progressbar_inner_radius_ratio" + android:shape="arc" + android:useLevel="true" android:thickness="@dimen/progressbar_thickness" - android:useLevel="true"> - <solid android:color="@color/materialColorPrimary"/> + android:innerRadiusRatio="@dimen/progressbar_inner_radius_ratio" + android:strokeCap="round"> + <solid android:color="@*android:color/materialColorPrimary"/> </shape> </item> </layer-list> diff --git a/core/res/res/values-w192dp/dimens_watch.xml b/core/res/res/values-w192dp/dimens_watch.xml deleted file mode 100644 index c6bf767ab6b8..000000000000 --- a/core/res/res/values-w192dp/dimens_watch.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2025 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. - --> -<resources> - <!-- 16.7% of display size --> - <dimen name="base_error_dialog_top_padding">32dp</dimen> - <!-- 5.2% of display size --> - <dimen name="base_error_dialog_padding">10dp</dimen> - <!-- 20.83% of display size --> - <dimen name="base_error_dialog_bottom_padding">40dp</dimen> - - <!-- watch's indeterminate progress bar dimens based on the current screen size --> - <dimen name="loader_horizontal_min_width_watch">67dp</dimen> - <dimen name="loader_horizontal_min_height_watch">15dp</dimen> -</resources> diff --git a/core/res/res/values-w216dp/dimens_watch.xml b/core/res/res/values-w216dp/dimens_watch.xml deleted file mode 100644 index e14ce5e98f55..000000000000 --- a/core/res/res/values-w216dp/dimens_watch.xml +++ /dev/null @@ -1,21 +0,0 @@ -<!-- - ~ Copyright (C) 2025 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. - --> - -<resources> - <!-- watch's indeterminate progress bar dimens based on the current screen size --> - <dimen name="loader_horizontal_min_width_watch">72dp</dimen> - <dimen name="loader_horizontal_min_height_watch">16dp</dimen> -</resources>
\ No newline at end of file diff --git a/core/res/res/values-w228dp/dimens_watch.xml b/core/res/res/values-w228dp/dimens_watch.xml deleted file mode 100644 index 3c6265690c3c..000000000000 --- a/core/res/res/values-w228dp/dimens_watch.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2025 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. - --> -<resources> - <!-- watch's indeterminate progress bar dimens based on the current screen size --> - <dimen name="loader_horizontal_min_width">76dp</dimen> - <dimen name="loader_horizontal_min_height">14dp</dimen> -</resources> diff --git a/core/res/res/values-w240dp/dimens_material.xml b/core/res/res/values-w240dp/dimens_material.xml index e30aea46aec7..bd26c8bf84df 100644 --- a/core/res/res/values-w240dp/dimens_material.xml +++ b/core/res/res/values-w240dp/dimens_material.xml @@ -21,8 +21,4 @@ <dimen name="screen_percentage_12">28.8dp</dimen> <dimen name="screen_percentage_15">36dp</dimen> <dimen name="screen_percentage_3646">87.5dp</dimen> - - <!-- watch's indeterminate progress bar dimens based on the current screen size --> - <dimen name="progress_indeterminate_horizontal_min_width_watch">80dp</dimen> - <dimen name="progress_indeterminate_horizontal_min_height_watch">17dp</dimen> </resources> diff --git a/core/res/res/values-watch/styles_device_defaults.xml b/core/res/res/values-watch/styles_device_defaults.xml index eeb66e7cf6a8..fb7dbb0660c5 100644 --- a/core/res/res/values-watch/styles_device_defaults.xml +++ b/core/res/res/values-watch/styles_device_defaults.xml @@ -42,8 +42,5 @@ <item name="indeterminateOnly">false</item> <!-- Use Wear Material3 ring shape as default determinate drawable --> <item name="progressDrawable">@drawable/progress_ring_watch</item> - <item name="indeterminateDrawable">@drawable/loader_horizontal_watch</item> - <item name="android:minWidth">@dimen/loader_horizontal_min_width_watch</item> - <item name="android:minHeight">@dimen/loader_horizontal_min_height_watch</item> </style> </resources> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 66111785af4f..d2c993aecb0d 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -6934,6 +6934,8 @@ <enum name="line" value="2" /> <!-- Ring shape. --> <enum name="ring" value="3" /> + <!-- ARC shape. --> + <enum name="arc" value="4"/> </attr> <!-- Inner radius of the ring expressed as a ratio of the ring's width. For instance, if innerRadiusRatio=9, then the inner radius equals the ring's width divided by 9. @@ -6966,6 +6968,12 @@ <attr name="opticalInsetRight" /> <!-- Bottom optical inset. --> <attr name="opticalInsetBottom" /> + <!-- Attributes that customize the stroke line cap. @hide --> + <attr name="strokeCap" format="enum"> + <enum name="butt" value="0"/> + <enum name="round" value="1"/> + <enum name="square" value="2"/> + </attr> </declare-styleable> <!-- Used to specify the size of the shape for GradientDrawable. --> diff --git a/core/res/res/values/dimens_watch.xml b/core/res/res/values/dimens_watch.xml index 19845916984b..7462b733b0ae 100644 --- a/core/res/res/values/dimens_watch.xml +++ b/core/res/res/values/dimens_watch.xml @@ -52,7 +52,7 @@ <dimen name="primary_content_alpha_device_default">0.38</dimen> <!-- values for wear material3 progress bar(progress indicator) --> - <item name="progressbar_inner_radius_ratio" format="float" type="dimen">2.12</item> + <item name="progressbar_inner_radius_ratio" format="float" type="dimen">2</item> <dimen name="progressbar_thickness">8dp</dimen> <dimen name="progressbar_elevation">0.1dp</dimen> @@ -61,8 +61,4 @@ <dimen name="disabled_alpha_wear_material3">0.12</dimen> <!-- Alpha transparency applied to elements which are considered primary (e.g. primary text) --> <dimen name="primary_content_alpha_wear_material3">0.38</dimen> - - <!-- watch's indeterminate progress bar dimens --> - <dimen name="loader_horizontal_min_width">68dp</dimen> - <dimen name="loader_horizontal_min_height">13dp</dimen> </resources> diff --git a/core/tests/coretests/src/android/os/PerfettoTraceTest.java b/core/tests/coretests/src/android/os/PerfettoTraceTest.java index c5c2554c2a67..fb743d2b42ad 100644 --- a/core/tests/coretests/src/android/os/PerfettoTraceTest.java +++ b/core/tests/coretests/src/android/os/PerfettoTraceTest.java @@ -77,6 +77,8 @@ public class PerfettoTraceTest { private static final String TAG = "PerfettoTraceTest"; private static final String FOO = "foo"; private static final String BAR = "bar"; + private static final String TEXT_ABOVE_4K_SIZE = + new String(new char[8192]).replace('\0', 'a'); private static final Category FOO_CATEGORY = new Category(FOO); private static final int MESSAGE = 1234567; @@ -155,41 +157,6 @@ public class PerfettoTraceTest { @Test @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2) - public void testDebugAnnotationsWithLambda() throws Exception { - TraceConfig traceConfig = getTraceConfig(FOO); - - PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray()); - - PerfettoTrace.instant(FOO_CATEGORY, "event").addArg("long_val", 123L).emit(); - - byte[] traceBytes = session.close(); - - Trace trace = Trace.parseFrom(traceBytes); - - boolean hasTrackEvent = false; - boolean hasDebugAnnotations = false; - for (TracePacket packet: trace.getPacketList()) { - TrackEvent event; - if (packet.hasTrackEvent()) { - hasTrackEvent = true; - event = packet.getTrackEvent(); - - if (TrackEvent.Type.TYPE_INSTANT.equals(event.getType()) - && event.getDebugAnnotationsCount() == 1) { - hasDebugAnnotations = true; - - List<DebugAnnotation> annotations = event.getDebugAnnotationsList(); - assertThat(annotations.get(0).getIntValue()).isEqualTo(123L); - } - } - } - - assertThat(hasTrackEvent).isTrue(); - assertThat(hasDebugAnnotations).isTrue(); - } - - @Test - @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2) public void testNamedTrack() throws Exception { TraceConfig traceConfig = getTraceConfig(FOO); @@ -440,7 +407,6 @@ public class PerfettoTraceTest { boolean hasTrackEvent = false; boolean hasSourceLocation = false; - for (TracePacket packet: trace.getPacketList()) { TrackEvent event; if (packet.hasTrackEvent()) { @@ -467,6 +433,53 @@ public class PerfettoTraceTest { @Test @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2) + public void testProtoWithSlowPath() throws Exception { + TraceConfig traceConfig = getTraceConfig(FOO); + + PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray()); + + PerfettoTrace.instant(FOO_CATEGORY, "event_proto") + .beginProto() + .beginNested(33L) + .addField(4L, 2L) + .addField(3, TEXT_ABOVE_4K_SIZE) + .endNested() + .addField(2001, "AIDL::IActivityManager") + .endProto() + .emit(); + + byte[] traceBytes = session.close(); + + Trace trace = Trace.parseFrom(traceBytes); + + boolean hasTrackEvent = false; + boolean hasSourceLocation = false; + for (TracePacket packet: trace.getPacketList()) { + TrackEvent event; + if (packet.hasTrackEvent()) { + hasTrackEvent = true; + event = packet.getTrackEvent(); + + if (TrackEvent.Type.TYPE_INSTANT.equals(event.getType()) + && event.hasSourceLocation()) { + SourceLocation loc = event.getSourceLocation(); + if (TEXT_ABOVE_4K_SIZE.equals(loc.getFunctionName()) + && loc.getLineNumber() == 2) { + hasSourceLocation = true; + } + } + } + + collectInternedData(packet); + } + + assertThat(hasTrackEvent).isTrue(); + assertThat(hasSourceLocation).isTrue(); + assertThat(mCategoryNames).contains(FOO); + } + + @Test + @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2) public void testProtoNested() throws Exception { TraceConfig traceConfig = getTraceConfig(FOO); diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index a289df0441e5..c40137f1bd34 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -16,8 +16,6 @@ package android.view; -import static android.app.UiModeManager.MODE_NIGHT_NO; -import static android.app.UiModeManager.MODE_NIGHT_YES; import static android.util.SequenceUtils.getInitSeq; import static android.view.HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING; import static android.view.InputDevice.SOURCE_ROTARY_ENCODER; @@ -69,10 +67,8 @@ import static org.junit.Assume.assumeTrue; import android.annotation.NonNull; import android.app.Instrumentation; import android.app.UiModeManager; -import android.app.UiModeManager.ForceInvertType; import android.content.Context; import android.graphics.ForceDarkType; -import android.graphics.ForceDarkType.ForceDarkTypeDef; import android.graphics.Rect; import android.hardware.display.DisplayManagerGlobal; import android.os.Binder; @@ -97,12 +93,9 @@ import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import com.android.compatibility.common.util.ShellIdentityUtils; -import com.android.compatibility.common.util.TestUtils; import com.android.cts.input.BlockingQueueEventVerifier; import com.android.window.flags.Flags; -import com.google.common.truth.Expect; - import org.hamcrest.Matcher; import org.junit.After; import org.junit.AfterClass; @@ -131,8 +124,6 @@ public class ViewRootImplTest { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); - @Rule - public final Expect mExpect = Expect.create(); private ViewRootImpl mViewRootImpl; private View mView; @@ -1516,34 +1507,49 @@ public class ViewRootImplTest { } @Test - @RequiresFlagsEnabled(FLAG_FORCE_INVERT_COLOR) - public void updateConfiguration_returnsExpectedForceDarkMode() { - waitForSystemNightModeActivated(true); - - verifyForceDarkType(/* isAppInNightMode= */ true, /* isForceInvertEnabled= */ true, - UiModeManager.FORCE_INVERT_TYPE_DARK, ForceDarkType.FORCE_INVERT_COLOR_DARK); - verifyForceDarkType(/* isAppInNightMode= */ true, /* isForceInvertEnabled= */ false, - UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE); - verifyForceDarkType(/* isAppInNightMode= */ false, /* isForceInvertEnabled= */ true, - UiModeManager.FORCE_INVERT_TYPE_DARK, ForceDarkType.FORCE_INVERT_COLOR_DARK); - verifyForceDarkType(/* isAppInNightMode= */ false, /* isForceInvertEnabled= */ false, - UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE); - - waitForSystemNightModeActivated(false); - - verifyForceDarkType(/* isAppInNightMode= */ true, /* isForceInvertEnabled= */ true, - UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE); - verifyForceDarkType(/* isAppInNightMode= */ true, /* isForceInvertEnabled= */ false, - UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE); - verifyForceDarkType(/* isAppInNightMode= */ false, /* isForceInvertEnabled= */ true, - UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE); - verifyForceDarkType(/* isAppInNightMode= */ false, /* isForceInvertEnabled= */ false, - UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE); + public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() { + mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR); + ShellIdentityUtils.invokeWithShellPermissions(() -> { + Settings.Secure.putInt( + sContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, + /* value= */ 0 + ); + var uiModeManager = sContext.getSystemService(UiModeManager.class); + uiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO); + }); + + sInstrumentation.runOnMainSync(() -> + mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()) + ); + + assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.NONE); + } + + @Test + public void forceInvertOnDarkThemeOff_forceDarkModeEnabled() { + mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR); + ShellIdentityUtils.invokeWithShellPermissions(() -> { + Settings.Secure.putInt( + sContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, + /* value= */ 1 + ); + var uiModeManager = sContext.getSystemService(UiModeManager.class); + uiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO); + }); + + sInstrumentation.runOnMainSync(() -> + mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()) + ); + + assertThat(mViewRootImpl.determineForceDarkType()) + .isEqualTo(ForceDarkType.FORCE_INVERT_COLOR_DARK); } @Test - @EnableFlags(FLAG_FORCE_INVERT_COLOR) public void forceInvertOffForceDarkOff_forceDarkModeDisabled() { + mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR); ShellIdentityUtils.invokeWithShellPermissions(() -> { Settings.Secure.putInt( sContext.getContentResolver(), @@ -1556,14 +1562,15 @@ public class ViewRootImplTest { }); sInstrumentation.runOnMainSync(() -> - mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())); + mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()) + ); assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.NONE); } @Test - @EnableFlags(FLAG_FORCE_INVERT_COLOR) public void forceInvertOffForceDarkOn_forceDarkModeEnabled() { + mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR); ShellIdentityUtils.invokeWithShellPermissions(() -> { Settings.Secure.putInt( sContext.getContentResolver(), @@ -1575,7 +1582,8 @@ public class ViewRootImplTest { }); sInstrumentation.runOnMainSync(() -> - mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())); + mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()) + ); assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.FORCE_DARK); } @@ -1782,39 +1790,4 @@ public class ViewRootImplTest { () -> view.getViewTreeObserver().removeOnDrawListener(listener)); } } - - private void waitForSystemNightModeActivated(boolean active) { - ShellIdentityUtils.invokeWithShellPermissions(() -> - sInstrumentation.runOnMainSync(() -> { - var uiModeManager = sContext.getSystemService(UiModeManager.class); - uiModeManager.setNightModeActivated(active); - })); - sInstrumentation.waitForIdleSync(); - } - - private void verifyForceDarkType(boolean isAppInNightMode, boolean isForceInvertEnabled, - @ForceInvertType int expectedForceInvertType, - @ForceDarkTypeDef int expectedForceDarkType) { - var uiModeManager = sContext.getSystemService(UiModeManager.class); - ShellIdentityUtils.invokeWithShellPermissions(() -> { - uiModeManager.setApplicationNightMode( - isAppInNightMode ? MODE_NIGHT_YES : MODE_NIGHT_NO); - Settings.Secure.putInt( - sContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, - isForceInvertEnabled ? 1 : 0); - }); - - sInstrumentation.runOnMainSync(() -> - mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())); - try { - TestUtils.waitUntil("Waiting for force invert state changed", - () -> (uiModeManager.getForceInvertState() == expectedForceInvertType)); - } catch (Exception e) { - Log.e(TAG, "Unexpected error trying to apply force invert state. " + e); - e.printStackTrace(); - } - - mExpect.that(mViewRootImpl.determineForceDarkType()).isEqualTo(expectedForceDarkType); - } } diff --git a/core/tests/coretests/src/android/view/ViewRootRectTrackerTest.java b/core/tests/coretests/src/android/view/ViewRootRectTrackerTest.java new file mode 100644 index 000000000000..c66e10079bc8 --- /dev/null +++ b/core/tests/coretests/src/android/view/ViewRootRectTrackerTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2025 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 android.view; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.testng.AssertJUnit.assertEquals; + +import android.graphics.Rect; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.google.common.collect.Lists; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Collections; +import java.util.List; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class ViewRootRectTrackerTest { + private ViewRootRectTracker mTracker; + private final List<Rect> mRects = Lists.newArrayList(new Rect(0, 0, 5, 5), + new Rect(5, 5, 10, 10)); + + @Before + public void setUp() { + mTracker = new ViewRootRectTracker(v -> Collections.emptyList()); + } + + @Test + public void setRootRectsAndComputeTest() { + mTracker.setRootRects(mRects); + mTracker.computeChanges(); + assertEquals(mRects, mTracker.getLastComputedRects()); + } + + @Test + public void waitingForComputeChangesTest() { + mTracker.setRootRects(mRects); + assertTrue(mTracker.isWaitingForComputeChanges()); + mTracker.computeChangedRects(); + assertFalse(mTracker.isWaitingForComputeChanges()); + + View mockView = mock(View.class); + mTracker.updateRectsForView(mockView); + assertTrue(mTracker.isWaitingForComputeChanges()); + mTracker.computeChangedRects(); + assertFalse(mTracker.isWaitingForComputeChanges()); + } +} diff --git a/graphics/java/android/framework_graphics.aconfig b/graphics/java/android/framework_graphics.aconfig index a63cbee4d707..fdbee3ccbebe 100644 --- a/graphics/java/android/framework_graphics.aconfig +++ b/graphics/java/android/framework_graphics.aconfig @@ -42,3 +42,11 @@ flag { description: "Add DISPLAY_BT2020 ColorSpace support" bug: "344038816" } + +flag { + name: "gradient_drawable_shape_rounded_cap" + is_fixed_read_only: true + namespace: "core_graphics" + description: "Make GradientDrawable support drawing ring with rounded stroke cap." + bug: "380000245" +} diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java index 65854dd51a91..ef6b72871415 100644 --- a/graphics/java/android/graphics/HardwareRenderer.java +++ b/graphics/java/android/graphics/HardwareRenderer.java @@ -658,6 +658,13 @@ public class HardwareRenderer { } /** + * @hide + */ + public void addObserver(long nativeObserver) { + nAddObserver(mNativeProxy, nativeObserver); + } + + /** * TODO: Public API this? * * @hide @@ -667,6 +674,13 @@ public class HardwareRenderer { } /** + * @hide + */ + public void removeObserver(long nativeObserver) { + nRemoveObserver(mNativeProxy, nativeObserver); + } + + /** * Sets the desired color mode on this renderer. Whether or not the actual rendering * will use the requested colorMode depends on the hardware support for such rendering. * diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java index 29d033e64aea..ff1dc93d787b 100644 --- a/graphics/java/android/graphics/drawable/GradientDrawable.java +++ b/graphics/java/android/graphics/drawable/GradientDrawable.java @@ -16,7 +16,11 @@ package android.graphics.drawable; +import static com.android.graphics.flags.Flags.FLAG_GRADIENT_DRAWABLE_SHAPE_ROUNDED_CAP; +import static com.android.graphics.flags.Flags.gradientDrawableShapeRoundedCap; + import android.annotation.ColorInt; +import android.annotation.FlaggedApi; import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.NonNull; @@ -125,8 +129,14 @@ public class GradientDrawable extends Drawable { */ public static final int RING = 3; + /** + * Shape is an arc. + */ + @FlaggedApi(FLAG_GRADIENT_DRAWABLE_SHAPE_ROUNDED_CAP) + public static final int ARC = 4; + /** @hide */ - @IntDef({RECTANGLE, OVAL, LINE, RING}) + @IntDef({RECTANGLE, OVAL, LINE, RING, ARC}) @Retention(RetentionPolicy.SOURCE) public @interface Shape {} @@ -167,6 +177,17 @@ public class GradientDrawable extends Drawable { @Retention(RetentionPolicy.SOURCE) public @interface RadiusType {} + private static final int BUTT = 0; + + private static final int ROUND = 1; + + private static final int SQUARE = 2; + + /** @hide */ + @IntDef({BUTT, ROUND, SQUARE}) + @Retention(RetentionPolicy.SOURCE) + public @interface StrokeCap {} + private static final float DEFAULT_INNER_RADIUS_RATIO = 3.0f; private static final float DEFAULT_THICKNESS_RATIO = 9.0f; @@ -191,6 +212,8 @@ public class GradientDrawable extends Drawable { private boolean mMutated; private Path mRingPath; private boolean mPathIsDirty = true; + private Path mArcPath; + private Path mArcOutlinePath; /** Current gradient radius, valid when {@link #mGradientIsDirty} is false. */ private float mGradientRadius; @@ -850,6 +873,55 @@ public class GradientDrawable extends Drawable { } break; } + case ARC: + if (gradientDrawableShapeRoundedCap()) { + // TODO(b/394988176): Consider applying ARC drawing logic to RING shape. + float centerX = mRect.centerX(); + float centerY = mRect.centerY(); + float thickness = + st.mThickness != -1 ? st.mThickness + : mRect.width() / st.mThicknessRatio; + float radius = st.mInnerRadius != -1 ? st.mInnerRadius + : mRect.width() / st.mInnerRadiusRatio; + radius -= thickness; + float sweep = st.mUseLevelForShape ? (360.0f * getLevel() / 10000.0f) : 360f; + mRect.set(centerX - radius, centerY - radius, centerX + radius, + centerY + radius); + + // Prepare paint. Set style to STROKE for purpose of drawing line ARC. + mFillPaint.setStyle(Paint.Style.STROKE); + mFillPaint.setStrokeWidth(thickness); + mFillPaint.setStrokeCap(getStrokeLineCapForPaint(st.mStrokeCap)); + canvas.drawArc(mRect, 0.0f, sweep, /* useCenter= */ false, mFillPaint); + + if (haveStroke) { + if (mArcPath == null) { + mArcPath = new Path(); + } else { + mArcPath.reset(); + } + if (mArcOutlinePath == null) { + mArcOutlinePath = new Path(); + } else { + mArcOutlinePath.reset(); + } + if (sweep == 360f) { + mArcPath.addOval(mRect, Path.Direction.CW); + } else { + mArcPath.arcTo(mRect, 0.0f, sweep, /* forceMoveTo= */ false); + } + + // The arc path doesn't have width. So, to get the outline of the result arc + // shape, we need to apply the paint effect to the path; then use the + // output as the result outline. + mFillPaint.getFillPath(mArcPath, mArcOutlinePath); + canvas.drawPath(mArcOutlinePath, mStrokePaint); + } + + // Restore to FILL + mFillPaint.setStyle(Paint.Style.FILL); + break; + } case RING: Path path = buildRing(st); canvas.drawPath(path, mFillPaint); @@ -993,6 +1065,36 @@ public class GradientDrawable extends Drawable { } /** + * Return current drawable's stroke line cap. Note that this is only respected when drawable is + * {@link Shape#ARC}. + * + * @return the {@link StrokeCap} of current drawable. + * @attr ref android.R.styleable#GradientDrawable_strokeCap + * @see #setStrokeCap(int) + * + * @hide + */ + @StrokeCap + public int getStrokeCap() { + return mGradientState.mStrokeCap; + } + + /** + * Configure the stroke line cap type that drawable will use while drawing. Note that this is + * only respected when drawable is {@link Shape#ARC}. + * + * @param strokeCapType the stroke line cap type that the drawable will use while drawing. + * @attr ref android.R.styleable#GradientDrawable_strokeCap + * @see #getStrokeCap + * + * @hide + */ + public void setStrokeCap(@StrokeCap int strokeCapType) { + mGradientState.mStrokeCap = strokeCapType; + invalidateSelf(); + } + + /** * Configure the padding of the gradient shape * @param left Left padding of the gradient shape * @param top Top padding of the gradient shape @@ -1066,6 +1168,15 @@ public class GradientDrawable extends Drawable { return ringPath; } + private Paint.Cap getStrokeLineCapForPaint(@StrokeCap int strokeLineCap) { + return switch (strokeLineCap) { + case BUTT -> Paint.Cap.BUTT; + case ROUND -> Paint.Cap.ROUND; + case SQUARE -> Paint.Cap.SQUARE; + default -> Paint.Cap.SQUARE; + }; + } + /** * Changes this drawable to use a single color instead of a gradient. * <p> @@ -1484,7 +1595,7 @@ public class GradientDrawable extends Drawable { state.mShape = a.getInt(R.styleable.GradientDrawable_shape, state.mShape); state.mDither = a.getBoolean(R.styleable.GradientDrawable_dither, state.mDither); - if (state.mShape == RING) { + if (state.mShape == RING || state.mShape == ARC) { state.mInnerRadius = a.getDimensionPixelSize( R.styleable.GradientDrawable_innerRadius, state.mInnerRadius); @@ -1503,6 +1614,9 @@ public class GradientDrawable extends Drawable { state.mUseLevelForShape = a.getBoolean( R.styleable.GradientDrawable_useLevel, state.mUseLevelForShape); + + state.mStrokeCap = a.getInt( + R.styleable.GradientDrawable_strokeCap, state.mStrokeCap); } final int tintMode = a.getInt(R.styleable.GradientDrawable_tintMode, -1); @@ -2045,6 +2159,9 @@ public class GradientDrawable extends Drawable { public int mInnerRadius = -1; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050218) public int mThickness = -1; + @UnsupportedAppUsage(trackingBug = 380000245) + @StrokeCap public int mStrokeCap = ROUND; + public boolean mDither = false; public Insets mOpticalInsets = Insets.NONE; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index f6a2c8d9695e..305fcdd5fb7d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -912,7 +912,7 @@ public class BubbleController implements ConfigurationChangeListener, // TODO(b/393172431) : Utilise DragZoneFactory once it is ready final int bubbleBarDropZoneSideSize = getContext().getResources().getDimensionPixelSize( R.dimen.bubble_bar_drop_zone_side_size); - int top = t - bubbleBarDropZoneSideSize; + int top = b - bubbleBarDropZoneSideSize; result.put(BubbleBarLocation.LEFT, new Rect(l, top, l + bubbleBarDropZoneSideSize, b)); result.put(BubbleBarLocation.RIGHT, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java index e3b0872df593..29837dc04423 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java @@ -135,6 +135,7 @@ public class BubbleBarLayerView extends FrameLayout /** Shows the expanded view drop target at the requested {@link BubbleBarLocation location} */ public void showBubbleBarExtendedViewDropTarget(@NonNull BubbleBarLocation bubbleBarLocation) { + setVisibility(VISIBLE); mBubbleExpandedViewPinController.showDropTarget(bubbleBarLocation); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt index b46051c51fcc..cb231800bd63 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt @@ -43,7 +43,7 @@ import com.android.wm.shell.bubbles.BubbleTransitions import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP -import com.android.wm.shell.protolog.ShellProtoLogGroup +import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.shared.TransitionUtil import com.android.wm.shell.shared.animation.PhysicsAnimator import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT @@ -120,10 +120,7 @@ sealed class DragToDesktopTransitionHandler( dragToDesktopAnimator: MoveToDesktopAnimator, ) { if (inProgress) { - ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, - "DragToDesktop: Drag to desktop transition already in progress.", - ) + logV("Drag to desktop transition already in progress.") return } @@ -537,12 +534,14 @@ sealed class DragToDesktopTransitionHandler( state.cancelState == CancelState.CANCEL_SPLIT_LEFT || state.cancelState == CancelState.CANCEL_SPLIT_RIGHT ) { + logV("mergeAnimation: cancel through split") clearState() return } // In case of bubble animation, finish the initial desktop drag animation, but keep the // current animation running and have bubbles take over if (info.type == TRANSIT_CONVERT_TO_BUBBLE) { + logV("mergeAnimation: convert-to-bubble") state.startTransitionFinishCb?.onTransitionFinished(/* wct= */ null) clearState() return @@ -562,6 +561,7 @@ sealed class DragToDesktopTransitionHandler( state.startTransitionFinishCb ?: error("Start transition expected to be waiting for merge but wasn't") if (isEndTransition) { + logV("mergeAnimation: end-transition, target=$mergeTarget") setupEndDragToDesktop( info, startTransaction = startT, @@ -572,7 +572,10 @@ sealed class DragToDesktopTransitionHandler( LatencyTracker.getInstance(context) .onActionEnd(LatencyTracker.ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG) animateEndDragToDesktop(startTransaction = startT, startTransitionFinishCb) - } else if (isCancelTransition) { + return + } + if (isCancelTransition) { + logV("mergeAnimation: cancel-transition, target=$mergeTarget") LatencyTracker.getInstance(context) .onActionCancel(LatencyTracker.ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG) info.changes.forEach { change -> @@ -583,7 +586,9 @@ sealed class DragToDesktopTransitionHandler( finishCallback.onTransitionFinished(/* wct= */ null) startTransitionFinishCb.onTransitionFinished(/* wct= */ null) clearState() + return } + logW("unhandled merge transition: transitionInfo=$info") } protected open fun setupEndDragToDesktop( @@ -724,10 +729,7 @@ sealed class DragToDesktopTransitionHandler( return } if (state.startTransitionToken == transition) { - ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, - "DragToDesktop: onTransitionConsumed() start transition aborted", - ) + logV("onTransitionConsumed() start transition aborted") state.startAborted = true // The start-transition (DRAG_HOLD) is aborted, cancel its jank interaction. interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD) @@ -950,7 +952,16 @@ sealed class DragToDesktopTransitionHandler( CANCEL_BUBBLE_RIGHT, } + private fun logV(msg: String, vararg arguments: Any?) { + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) + } + + private fun logW(msg: String, vararg arguments: Any?) { + ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) + } + companion object { + private const val TAG = "DragToDesktopTransitionHandler" /** The duration of the animation to commit or cancel the drag-to-desktop gesture. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) const val DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS = 336L @@ -1052,10 +1063,7 @@ constructor( val state = requireTransitionState() val homeLeash = state.homeChange?.leash if (homeLeash == null) { - ProtoLog.e( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, - "DragToDesktop: home leash is null", - ) + logE("home leash is null") } else { // Hide home on finish to prevent flickering when wallpaper activity flag is enabled finishTransaction.hide(homeLeash) @@ -1096,6 +1104,12 @@ constructor( val startBoundsWithOffset = Rect(startBounds).apply { offset(startPosition.x.toInt(), startPosition.y.toInt()) } + logV( + "animateEndDragToDesktop: startBounds=$startBounds, endBounds=$endBounds, " + + "startScale=$startScale, startPosition=$startPosition, " + + "startBoundsWithOffset=$startBoundsWithOffset" + ) + dragToDesktopStateListener?.onCommitToDesktopAnimationStart() // Accept the merge by applying the merging transaction (applied by #showResizeVeil) // and finish callback. Show the veil and position the task at the first frame before @@ -1177,7 +1191,16 @@ constructor( .start() } + private fun logV(msg: String, vararg arguments: Any?) { + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) + } + + private fun logE(msg: String, vararg arguments: Any?) { + ProtoLog.e(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) + } + companion object { + private const val TAG = "SpringDragToDesktopTransitionHandler" /** The freeform tasks initial scale when committing the drag-to-desktop gesture. */ private val FREEFORM_TASKS_INITIAL_SCALE = propertyValue("freeform_tasks_initial_scale", scale = 100f, default = 0.9f) diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index f5e10d94452f..7a51c20f7672 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -44,6 +44,9 @@ namespace android { namespace { +constexpr int32_t kDefaultDisplayId = 0; +constexpr int32_t kDefaultDeviceId = 0; + using EntryValue = std::variant<Res_value, incfs::verified_map_ptr<ResTable_map_entry>>; /* NOTE: table_entry has been verified in LoadedPackage::GetEntryFromOffset(), @@ -61,7 +64,7 @@ base::expected<EntryValue, IOError> GetEntryValue( return table_entry->value(); } -} // namespace +} // namespace struct FindEntryResult { // The cookie representing the ApkAssets in which the value resides. @@ -99,14 +102,15 @@ struct Theme::Entry { Res_value value; }; -AssetManager2::AssetManager2(ApkAssetsList apk_assets, const ResTable_config& configuration) { +AssetManager2::AssetManager2(ApkAssetsList apk_assets, const ResTable_config& configuration) + : display_id_(kDefaultDisplayId), device_id_(kDefaultDeviceId) { configurations_.push_back(configuration); // Don't invalidate caches here as there's nothing cached yet. SetApkAssets(apk_assets, false); } -AssetManager2::AssetManager2() { +AssetManager2::AssetManager2() : display_id_(kDefaultDisplayId), device_id_(kDefaultDeviceId) { configurations_.emplace_back(); } @@ -172,8 +176,7 @@ void AssetManager2::BuildDynamicRefTable(ApkAssetsList apk_assets) { // to take effect. auto iter = target_assets_package_ids.find(loaded_idmap->TargetApkPath()); if (iter == target_assets_package_ids.end()) { - LOG(INFO) << "failed to find target package for overlay " - << loaded_idmap->OverlayApkPath(); + LOG(INFO) << "failed to find target package for overlay " << loaded_idmap->OverlayApkPath(); } else { uint8_t target_package_id = iter->second; @@ -189,10 +192,11 @@ void AssetManager2::BuildDynamicRefTable(ApkAssetsList apk_assets) { << " assigned package group"; PackageGroup& target_package_group = package_groups_[target_idx]; - target_package_group.overlays_.push_back( - ConfiguredOverlay{loaded_idmap->GetTargetResourcesMap(target_package_id, - overlay_ref_table.get()), - apk_assets_cookies[apk_assets]}); + target_package_group.overlays_.push_back(ConfiguredOverlay{ + loaded_idmap->GetTargetResourcesMap(target_package_id, overlay_ref_table.get()), + apk_assets_cookies[apk_assets], + IsAnyOverlayConstraintSatisfied(loaded_idmap->GetConstraints()) + }); } } @@ -291,7 +295,7 @@ void AssetManager2::DumpToLog() const { } LOG(INFO) << "Package ID map: " << list; - for (const auto& package_group: package_groups_) { + for (const auto& package_group : package_groups_) { list = ""; for (const auto& package : package_group.packages_) { const LoadedPackage* loaded_package = package.loaded_package_; @@ -347,7 +351,6 @@ std::shared_ptr<const DynamicRefTable> AssetManager2::GetDynamicRefTableForCooki const std::unordered_map<std::string, std::string>* AssetManager2::GetOverlayableMapForPackage(uint32_t package_id) const { - if (package_id >= package_ids_.size()) { return nullptr; } @@ -462,6 +465,28 @@ void AssetManager2::SetConfigurations(std::span<const ResTable_config> configura } } +void AssetManager2::SetOverlayConstraints(int32_t display_id, int32_t device_id) { + bool changed = false; + if (display_id_ != display_id) { + display_id_ = display_id; + changed = true; + } + if (device_id_ != device_id) { + device_id_ = device_id; + changed = true; + } + if (changed) { + // Enable/disable overlays based on current constraints + for (PackageGroup& group : package_groups_) { + for (auto &overlay: group.overlays_) { + overlay.enabled = IsAnyOverlayConstraintSatisfied( + overlay.overlay_res_maps_.GetConstraints()); + } + } + InvalidateCaches(static_cast<uint32_t>(-1)); + } +} + std::set<AssetManager2::ApkAssetsPtr> AssetManager2::GetNonSystemOverlays() const { std::set<ApkAssetsPtr> non_system_overlays; for (const PackageGroup& package_group : package_groups_) { @@ -475,6 +500,8 @@ std::set<AssetManager2::ApkAssetsPtr> AssetManager2::GetNonSystemOverlays() cons if (!found_system_package) { auto op = StartOperation(); + // Return all overlays, including the disabled ones as this is used for static info + // collection only. for (const ConfiguredOverlay& overlay : package_group.overlays_) { if (const auto& asset = GetApkAssets(overlay.cookie)) { non_system_overlays.insert(std::move(asset)); @@ -651,7 +678,6 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry( auto op = StartOperation(); - // Retrieve the package group from the package id of the resource id. if (UNLIKELY(!is_valid_resid(resid))) { LOG(ERROR) << base::StringPrintf("Invalid resource ID 0x%08x.", resid); @@ -672,7 +698,7 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry( std::optional<FindEntryResult> final_result; bool final_has_locale = false; bool final_overlaid = false; - for (auto & config : configurations_) { + for (auto& config : configurations_) { // Might use this if density_override != 0. ResTable_config density_override_config; @@ -698,7 +724,8 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry( } if (!assets->IsLoader()) { for (const auto& id_map : package_group.overlays_) { - auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid); + auto overlay_entry = id_map.enabled ? + id_map.overlay_res_maps_.Lookup(resid) : IdmapResMap::Result(); if (!overlay_entry) { // No id map entry exists for this target resource. continue; @@ -708,7 +735,7 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry( ConfigDescription best_frro_config; Res_value best_frro_value; bool frro_found = false; - for( const auto& [config, value] : overlay_entry.GetInlineValue()) { + for (const auto& [config, value] : overlay_entry.GetInlineValue()) { if ((!frro_found || config.isBetterThan(best_frro_config, desired_config)) && config.match(*desired_config)) { frro_found = true; @@ -1011,7 +1038,7 @@ std::string AssetManager2::GetLastResourceResolution() const { resid, resource_name_string.c_str(), conf.toString().c_str()); char str[40]; str[0] = '\0'; - for(auto iter = configurations_.begin(); iter < configurations_.end(); iter++) { + for (auto iter = configurations_.begin(); iter < configurations_.end(); iter++) { iter->getBcp47Locale(str); log_stream << base::StringPrintf(" %s%s", str, iter < configurations_.end() ? "," : ""); } @@ -1504,7 +1531,7 @@ void AssetManager2::RebuildFilterList() { package.loaded_package_->ForEachTypeSpec([&](const TypeSpec& type_spec, uint8_t type_id) { FilteredConfigGroup* group = nullptr; for (const auto& type_entry : type_spec.type_entries) { - for (auto & config : configurations_) { + for (auto& config : configurations_) { if (type_entry.config.match(config)) { if (!group) { group = &package.filtered_configs_.editItemAt(type_id - 1); @@ -1521,6 +1548,27 @@ void AssetManager2::RebuildFilterList() { } } +bool AssetManager2::IsAnyOverlayConstraintSatisfied(const Idmap_constraints& constraints) const { + if (constraints.constraint_count == 0) { + // There are no constraints, return true. + return true; + } + + for (uint32_t i = 0; i < constraints.constraint_count; i++) { + auto constraint = constraints.constraint_entries[i]; + if (constraint.constraint_type == kOverlayConstraintTypeDisplayId && + constraint.constraint_value == display_id_) { + return true; + } + if (constraint.constraint_type == kOverlayConstraintTypeDeviceId && + constraint.constraint_value == device_id_) { + return true; + } + } + + return false; +} + void AssetManager2::InvalidateCaches(uint32_t diff) { cached_resolved_values_.clear(); diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp index f0ef97e5bdcc..8d1de1af56d2 100644 --- a/libs/androidfw/Idmap.cpp +++ b/libs/androidfw/Idmap.cpp @@ -56,13 +56,6 @@ struct Idmap_header { // without having to read/store each header entry separately. }; -struct Idmap_constraint { - // Constraint type can be TYPE_DISPLAY_ID or TYP_DEVICE_ID, please refer - // to ConstraintType in OverlayConstraint.java - uint32_t constraint_type; - uint32_t constraint_value; -}; - struct Idmap_data_header { uint32_t target_entry_count; uint32_t target_inline_entry_count; @@ -148,12 +141,13 @@ status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) cons return DynamicRefTable::lookupResourceId(resId); } -IdmapResMap::IdmapResMap(const Idmap_data_header* data_header, Idmap_target_entries entries, - Idmap_target_inline_entries inline_entries, +IdmapResMap::IdmapResMap(const Idmap_data_header* data_header, const Idmap_constraints& constraints, + Idmap_target_entries entries, Idmap_target_inline_entries inline_entries, const Idmap_target_entry_inline_value* inline_entry_values, const ConfigDescription* configs, uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table) : data_header_(data_header), + constraints_(constraints), entries_(entries), inline_entries_(inline_entries), inline_entry_values_(inline_entry_values), @@ -254,7 +248,7 @@ std::optional<std::string_view> ReadString(const uint8_t** in_out_data_ptr, size } return std::string_view(data, *len); } -} // namespace +} // namespace // O_PATH is a lightweight way of creating an FD, only exists on Linux #ifndef O_PATH @@ -262,9 +256,7 @@ std::optional<std::string_view> ReadString(const uint8_t** in_out_data_ptr, size #endif LoadedIdmap::LoadedIdmap(const std::string& idmap_path, const Idmap_header* header, - const Idmap_constraint* constraints, - uint32_t constraints_count, - const Idmap_data_header* data_header, + const Idmap_data_header* data_header, const Idmap_constraints& constraints, Idmap_target_entries target_entries, Idmap_target_inline_entries target_inline_entries, const Idmap_target_entry_inline_value* inline_entry_values, @@ -272,9 +264,8 @@ LoadedIdmap::LoadedIdmap(const std::string& idmap_path, const Idmap_header* head std::unique_ptr<ResStringPool>&& string_pool, std::string_view overlay_apk_path, std::string_view target_apk_path) : header_(header), - constraints_(constraints), - constraints_count_(constraints_count), data_header_(data_header), + constraints_(constraints), target_entries_(target_entries), target_inline_entries_(target_inline_entries), inline_entry_values_(inline_entry_values), @@ -328,16 +319,20 @@ std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPie return {}; } - auto constraints_count = ReadType<uint32_t>(&data_ptr, &data_size, "constraints count"); - if (!constraints_count) { + auto constraint_count = ReadType<uint32_t>(&data_ptr, &data_size, "constraint count"); + if (!constraint_count) { + LOG(ERROR) << "idmap doesn't have constraint count"; return {}; } - auto constraints = *constraints_count > 0 ? - ReadType<Idmap_constraint>(&data_ptr, &data_size, "constraints", *constraints_count) + auto constraint_entries = *constraint_count > 0 ? + ReadType<Idmap_constraint>(&data_ptr, &data_size, "constraints", dtohl(*constraint_count)) : nullptr; - if (*constraints_count > 0 && !constraints) { + if (*constraint_count > 0 && !constraint_entries) { + LOG(ERROR) << "no constraint entries in idmap with non-zero constraints"; return {}; } + Idmap_constraints constraints{.constraint_count = *constraint_count, + .constraint_entries = constraint_entries}; // Parse the idmap data blocks. Currently idmap2 can only generate one data block. auto data_header = ReadType<Idmap_data_header>(&data_ptr, &data_size, "data header"); @@ -405,10 +400,9 @@ std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPie // Can't use make_unique because LoadedIdmap constructor is private. return std::unique_ptr<LoadedIdmap>( - new LoadedIdmap(std::string(idmap_path), header, constraints, *constraints_count, - data_header, target_entries, target_inline_entries, - target_inline_entry_values,configurations, overlay_entries, - std::move(idmap_string_pool),*overlay_path, *target_path)); + new LoadedIdmap(std::string(idmap_path), header, data_header, constraints, target_entries, + target_inline_entries, target_inline_entry_values, configurations, + overlay_entries, std::move(idmap_string_pool), *overlay_path, *target_path)); } UpToDate LoadedIdmap::IsUpToDate() const { diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 0fdeefa09e26..a47fe6a7f50d 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -171,6 +171,8 @@ class AssetManager2 { default_locale_ = default_locale; } + void SetOverlayConstraints(int32_t display_id, int32_t device_id); + // Returns all configurations for which there are resources defined, or an I/O error if reading // resource data failed. // @@ -389,6 +391,9 @@ class AssetManager2 { // The cookie of the overlay assets. ApkAssetsCookie cookie; + + // Enable/disable status of the overlay based on current constraints of AssetManager. + bool enabled; }; // Represents a logical package, which can be made up of many individual packages. Each package @@ -457,6 +462,8 @@ class AssetManager2 { // promoted apk assets when the last operation ends. void FinishOperation() const; + bool IsAnyOverlayConstraintSatisfied(const Idmap_constraints& constraints) const; + // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. // The second pair element is the promoted version of the assets, that is held for the duration @@ -480,6 +487,9 @@ class AssetManager2 { // may need to be purged. ftl::SmallVector<ResTable_config, 1> configurations_; + int32_t display_id_; + int32_t device_id_; + // Cached set of bags. These are cached because they can inherit keys from parent bags, // which involves some calculation. mutable std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_; diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h index 0c0856315d8f..939b62462560 100644 --- a/libs/androidfw/include/androidfw/Idmap.h +++ b/libs/androidfw/include/androidfw/Idmap.h @@ -59,13 +59,25 @@ inline UpToDate fromBool(bool value) { class LoadedIdmap; class IdmapResMap; struct Idmap_header; -struct Idmap_constraint; +struct Idmap_constraints; struct Idmap_data_header; -struct Idmap_target_entry; struct Idmap_target_entry_inline; struct Idmap_target_entry_inline_value; -struct Idmap_overlay_entry; +// LINT.IfChange +constexpr int32_t kOverlayConstraintTypeDisplayId = 0; +constexpr int32_t kOverlayConstraintTypeDeviceId = 1; +// LINT.ThenChange(../../../../core/java/android/content/om/OverlayConstraint.java) + +struct Idmap_constraint { + // Constraint type can be kOverlayConstraintTypeDisplayId or kOverlayConstraintTypeDeviceId + const uint32_t constraint_type; + const uint32_t constraint_value; +}; +struct Idmap_constraints { + const uint32_t constraint_count = 0; + const Idmap_constraint* constraint_entries = nullptr; +}; struct Idmap_target_entries { const uint32_t* target_id = nullptr; const uint32_t* overlay_id = nullptr; @@ -169,14 +181,19 @@ class IdmapResMap { return overlay_ref_table_; } + inline Idmap_constraints GetConstraints() const { + return constraints_; + } + private: - explicit IdmapResMap(const Idmap_data_header* data_header, Idmap_target_entries entries, - Idmap_target_inline_entries inline_entries, + explicit IdmapResMap(const Idmap_data_header* data_header, const Idmap_constraints& constraints, + Idmap_target_entries entries, Idmap_target_inline_entries inline_entries, const Idmap_target_entry_inline_value* inline_entry_values, const ConfigDescription* configs, uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table); const Idmap_data_header* data_header_; + Idmap_constraints constraints_; Idmap_target_entries entries_; Idmap_target_inline_entries inline_entries_; const Idmap_target_entry_inline_value* inline_entry_values_; @@ -210,8 +227,9 @@ class LoadedIdmap { // Returns a mapping from target resource ids to overlay values. IdmapResMap GetTargetResourcesMap(uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table) const { - return IdmapResMap(data_header_, target_entries_, target_inline_entries_, inline_entry_values_, - configurations_, target_assigned_package_id, overlay_ref_table); + return IdmapResMap(data_header_, constraints_, target_entries_, target_inline_entries_, + inline_entry_values_, configurations_, target_assigned_package_id, + overlay_ref_table); } // Returns a dynamic reference table for a loaded overlay package. @@ -223,14 +241,17 @@ class LoadedIdmap { // LoadedIdmap. UpToDate IsUpToDate() const; + inline const Idmap_constraints GetConstraints() const { + return constraints_; + } + protected: // Exposed as protected so that tests can subclass and mock this class out. LoadedIdmap() = default; const Idmap_header* header_; - const Idmap_constraint* constraints_; - uint32_t constraints_count_; const Idmap_data_header* data_header_; + Idmap_constraints constraints_; Idmap_target_entries target_entries_; Idmap_target_inline_entries target_inline_entries_; const Idmap_target_entry_inline_value* inline_entry_values_; @@ -247,9 +268,7 @@ class LoadedIdmap { DISALLOW_COPY_AND_ASSIGN(LoadedIdmap); explicit LoadedIdmap(const std::string& idmap_path, const Idmap_header* header, - const Idmap_constraint* constraints, - uint32_t constraints_count, - const Idmap_data_header* data_header, + const Idmap_data_header* data_header, const Idmap_constraints& constraints, Idmap_target_entries target_entries, Idmap_target_inline_entries target_inline_entries, const Idmap_target_entry_inline_value* inline_entry_values_, diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp index fa09296f647c..0e52f4e177c5 100644 --- a/libs/hwui/FrameInfo.cpp +++ b/libs/hwui/FrameInfo.cpp @@ -52,7 +52,7 @@ const std::array FrameInfoNames{"Flags", static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 24, "Must update value in FrameMetrics.java#FRAME_STATS_COUNT (and here)"); -void FrameInfo::importUiThreadInfo(int64_t* info) { +void FrameInfo::importUiThreadInfo(const int64_t* info) { memcpy(mFrameInfo.data(), info, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t)); mSkippedFrameReason.reset(); } diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h index 8b486cd49e24..b1f479faadff 100644 --- a/libs/hwui/FrameInfo.h +++ b/libs/hwui/FrameInfo.h @@ -134,7 +134,7 @@ private: class FrameInfo { public: - void importUiThreadInfo(int64_t* info); + void importUiThreadInfo(const int64_t* info); void markSyncStart() { set(FrameInfoIndex::SyncStart) = systemTime(SYSTEM_TIME_MONOTONIC); } diff --git a/libs/hwui/FrameMetricsReporter.h b/libs/hwui/FrameMetricsReporter.h index 0b0895aa3f3d..71b3f52fe12e 100644 --- a/libs/hwui/FrameMetricsReporter.h +++ b/libs/hwui/FrameMetricsReporter.h @@ -35,12 +35,12 @@ class FrameMetricsReporter { public: FrameMetricsReporter() {} - void addObserver(FrameMetricsObserver* observer) { + void addObserver(sp<FrameMetricsObserver>&& observer) { std::lock_guard lock(mObserversLock); - mObservers.push_back(observer); + mObservers.push_back(std::move(observer)); } - bool removeObserver(FrameMetricsObserver* observer) { + bool removeObserver(const sp<FrameMetricsObserver>& observer) { std::lock_guard lock(mObserversLock); for (size_t i = 0; i < mObservers.size(); i++) { if (mObservers[i].get() == observer) { diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index 99e7740d66d2..cfec24b17cd4 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -915,20 +915,22 @@ static jboolean android_view_ThreadedRenderer_isDrawingEnabled(JNIEnv*, jclass) static void android_view_ThreadedRenderer_addObserver(JNIEnv* env, jclass clazz, jlong proxyPtr, jlong observerPtr) { - HardwareRendererObserver* observer = reinterpret_cast<HardwareRendererObserver*>(observerPtr); + FrameMetricsObserver* rawObserver = reinterpret_cast<FrameMetricsObserver*>(observerPtr); + sp<FrameMetricsObserver> observer = sp<FrameMetricsObserver>::fromExisting(rawObserver); renderthread::RenderProxy* renderProxy = reinterpret_cast<renderthread::RenderProxy*>(proxyPtr); - renderProxy->addFrameMetricsObserver(observer); + renderProxy->addFrameMetricsObserver(std::move(observer)); } static void android_view_ThreadedRenderer_removeObserver(JNIEnv* env, jclass clazz, jlong proxyPtr, jlong observerPtr) { - HardwareRendererObserver* observer = reinterpret_cast<HardwareRendererObserver*>(observerPtr); + FrameMetricsObserver* rawObserver = reinterpret_cast<FrameMetricsObserver*>(observerPtr); + sp<FrameMetricsObserver> observer = sp<FrameMetricsObserver>::fromExisting(rawObserver); renderthread::RenderProxy* renderProxy = reinterpret_cast<renderthread::RenderProxy*>(proxyPtr); - renderProxy->removeFrameMetricsObserver(observer); + renderProxy->removeFrameMetricsObserver(std::move(observer)); } // ---------------------------------------------------------------------------- diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index e3e393c4fdfb..b248c4bc9ade 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -859,7 +859,7 @@ void CanvasContext::reportMetricsWithPresentTime() { } // release lock } -void CanvasContext::addFrameMetricsObserver(FrameMetricsObserver* observer) { +void CanvasContext::addFrameMetricsObserver(sp<FrameMetricsObserver>&& observer) { std::scoped_lock lock(mFrameInfoMutex); if (mFrameMetricsReporter.get() == nullptr) { mFrameMetricsReporter.reset(new FrameMetricsReporter()); @@ -870,10 +870,10 @@ void CanvasContext::addFrameMetricsObserver(FrameMetricsObserver* observer) { // their frame metrics. uint64_t nextFrameNumber = getFrameNumber(); observer->reportMetricsFrom(nextFrameNumber, mSurfaceControlGenerationId); - mFrameMetricsReporter->addObserver(observer); + mFrameMetricsReporter->addObserver(std::move(observer)); } -void CanvasContext::removeFrameMetricsObserver(FrameMetricsObserver* observer) { +void CanvasContext::removeFrameMetricsObserver(const sp<FrameMetricsObserver>& observer) { std::scoped_lock lock(mFrameInfoMutex); if (mFrameMetricsReporter.get() != nullptr) { mFrameMetricsReporter->removeObserver(observer); diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index cb3753822035..f119102dc2a2 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -176,8 +176,8 @@ public: void setContentDrawBounds(const Rect& bounds) { mContentDrawBounds = bounds; } - void addFrameMetricsObserver(FrameMetricsObserver* observer); - void removeFrameMetricsObserver(FrameMetricsObserver* observer); + void addFrameMetricsObserver(sp<FrameMetricsObserver>&& observer); + void removeFrameMetricsObserver(const sp<FrameMetricsObserver>& observer); // Used to queue up work that needs to be completed before this frame completes void enqueueFrameWork(std::function<void()>&& func); diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 77879cf992b7..ebfd8fde91f6 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -420,15 +420,15 @@ void RenderProxy::setFrameCompleteCallback(std::function<void()>&& callback) { mDrawFrameTask.setFrameCompleteCallback(std::move(callback)); } -void RenderProxy::addFrameMetricsObserver(FrameMetricsObserver* observerPtr) { - mRenderThread.queue().post([this, observer = sp{observerPtr}]() { - mContext->addFrameMetricsObserver(observer.get()); +void RenderProxy::addFrameMetricsObserver(sp<FrameMetricsObserver>&& observer) { + mRenderThread.queue().post([this, observer = std::move(observer)]() mutable { + mContext->addFrameMetricsObserver(std::move(observer)); }); } -void RenderProxy::removeFrameMetricsObserver(FrameMetricsObserver* observerPtr) { - mRenderThread.queue().post([this, observer = sp{observerPtr}]() { - mContext->removeFrameMetricsObserver(observer.get()); +void RenderProxy::removeFrameMetricsObserver(sp<FrameMetricsObserver>&& observer) { + mRenderThread.queue().post([this, observer = std::move(observer)]() { + mContext->removeFrameMetricsObserver(observer); }); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index f2d8e94c7bd2..ad6d54bfcf91 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -141,8 +141,8 @@ public: void setFrameCommitCallback(std::function<void(bool)>&& callback); void setFrameCompleteCallback(std::function<void()>&& callback); - void addFrameMetricsObserver(FrameMetricsObserver* observer); - void removeFrameMetricsObserver(FrameMetricsObserver* observer); + void addFrameMetricsObserver(sp<FrameMetricsObserver>&& observer); + void removeFrameMetricsObserver(sp<FrameMetricsObserver>&& observer); void setForceDark(ForceDarkType type); static void copySurfaceInto(ANativeWindow* window, std::shared_ptr<CopyRequest>&& request); diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java index 88981eac9bb5..1cb540b09fb3 100644 --- a/media/java/android/media/MediaRoute2Info.java +++ b/media/java/android/media/MediaRoute2Info.java @@ -481,9 +481,9 @@ public final class MediaRoute2Info implements Parcelable { /** * Indicates that a route supports routing playback to remote routes through control commands. * - * <p>This type of routing does not affect affect this system's audio or video, but instead - * relies on the device that corresponds to this route to fetch and play the media. It also - * requires the media app to take care of initializing and controlling playback. + * <p>This type of routing does not affect this system's audio or video, but instead relies on + * the device that corresponds to this route to fetch and play the media. It also requires the + * media app to take care of initializing and controlling playback. */ @FlaggedApi(FLAG_ENABLE_MIRRORING_IN_MEDIA_ROUTER_2) public static final int FLAG_ROUTING_TYPE_REMOTE = 1 << 2; diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java index e94fb7d9e52b..4ae8daa63e1d 100644 --- a/media/java/android/media/MediaRoute2ProviderService.java +++ b/media/java/android/media/MediaRoute2ProviderService.java @@ -348,18 +348,21 @@ public abstract class MediaRoute2ProviderService extends Service { * <p>This method must only be called as the result of a prior call to {@link * #onCreateSystemRoutingSession}. * + * <p>This method returns a {@link MediaStreams} instance that holds the media streams to route + * as part of the newly created routing session. May be null if system media capture failed, in + * which case you can ignore the return value, as you will receive a call to {@link + * #onReleaseSession} where you can clean up this session. {@link AudioRecord#startRecording()} + * must be called immediately on {@link MediaStreams#getAudioRecord()} after calling this + * method, in order to start streaming audio to the receiver. + * * @param requestId the ID of the {@link #onCreateSystemRoutingSession} request which this call * is in response to. * @param sessionInfo a {@link RoutingSessionInfo} that describes the newly created routing * session. * @param formats the {@link MediaStreamsFormats} that describes the format for the {@link * MediaStreams} to return. - * @return a {@link MediaStreams} instance that holds the media streams to route as part of the - * newly created routing session. May be null if system media capture failed, in which case - * you can ignore the return value, as you will receive a call to {@link #onReleaseSession} - * where you can clean up this session. {@link AudioRecord#startRecording()} must be called - * immediately on {@link MediaStreams#getAudioRecord()} after calling this method, in order - * to start streaming audio to the receiver. + * @return The {@link MediaStreams} to route as part of the new session, or null if system media + * capture failed and the result can be ignored. * @throws IllegalStateException If the provided {@code requestId} doesn't correspond to a * previous call to {@link #onCreateSystemRoutingSession}. */ @@ -1191,8 +1194,8 @@ public abstract class MediaRoute2ProviderService extends Service { * * <p>The default value is an empty {@link Bundle}. * - * <p>Note that this bundle is not copied, so avoiding mutating the given {@link Bundle} - * after passing it to this method. + * <p>Do not mutate the given {@link Bundle} after passing it to this method. You can + * use {@link Bundle#deepCopy()} to keep a mutable copy. */ @NonNull public Builder setExtras(@NonNull Bundle extras) { diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt index d6e19a6193fd..a75aeaff0c48 100644 --- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt +++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt @@ -104,6 +104,7 @@ fun WearApp( scrollable(Screen.MultipleCredentialsScreenFlatten.route) { MultiCredentialsFlattenScreen( credentialSelectorUiState = (remember { uiState } as MultipleEntry), + columnState = it.columnState, flowEngine = flowEngine, ) } diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt index 932b3456bfb8..96cadab3916a 100644 --- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt +++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt @@ -36,7 +36,7 @@ import com.android.credentialmanager.model.get.CredentialEntryInfo import com.android.credentialmanager.ui.components.CredentialsScreenChipSpacer import com.google.android.horologist.annotations.ExperimentalHorologistApi import com.google.android.horologist.compose.layout.ScalingLazyColumn -import com.google.android.horologist.compose.layout.rememberColumnState +import com.google.android.horologist.compose.layout.ScalingLazyColumnState import com.google.android.horologist.compose.layout.ScalingLazyColumnDefaults import androidx.compose.ui.text.style.TextAlign import androidx.wear.compose.material.MaterialTheme as WearMaterialTheme @@ -45,6 +45,7 @@ import androidx.wear.compose.material.MaterialTheme as WearMaterialTheme * Screen that shows multiple credentials to select from, grouped by accounts * * @param credentialSelectorUiState The app bar view model. + * @param columnState ScalingLazyColumn configuration to be be applied * @param modifier styling for composable * @param flowEngine [FlowEngine] that updates ui state for this screen */ @@ -52,15 +53,14 @@ import androidx.wear.compose.material.MaterialTheme as WearMaterialTheme @Composable fun MultiCredentialsFlattenScreen( credentialSelectorUiState: MultipleEntry, + columnState: ScalingLazyColumnState, flowEngine: FlowEngine, ) { val selectEntry = flowEngine.getEntrySelector() Row { Spacer(Modifier.weight(0.052f)) // 5.2% side margin ScalingLazyColumn( - columnState = rememberColumnState( - ScalingLazyColumnDefaults.belowTimeText(horizontalAlignment = Alignment.Start), - ), + columnState = columnState, modifier = Modifier.weight(0.896f).fillMaxSize(), // 5.2% side margin ) { diff --git a/packages/SettingsLib/AndroidManifest.xml b/packages/SettingsLib/AndroidManifest.xml index 412ff29aec32..473b7eb07666 100644 --- a/packages/SettingsLib/AndroidManifest.xml +++ b/packages/SettingsLib/AndroidManifest.xml @@ -21,6 +21,13 @@ <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> <application> + <activity + android:name=".users.CreateUserActivity" + android:excludeFromRecents="true" + android:exported="false" + android:finishOnCloseSystemDialogs="true" + android:launchMode="singleInstance" + android:theme="@style/Theme.Transparent"/> </application> </manifest> diff --git a/packages/SettingsLib/IllustrationPreference/AndroidManifest.xml b/packages/SettingsLib/IllustrationPreference/AndroidManifest.xml index a0d10c383445..56b05159a30f 100644 --- a/packages/SettingsLib/IllustrationPreference/AndroidManifest.xml +++ b/packages/SettingsLib/IllustrationPreference/AndroidManifest.xml @@ -18,6 +18,6 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.settingslib.widget.preference.illustration"> - <uses-sdk android:minSdkVersion="28" /> + <uses-sdk android:minSdkVersion="30" /> </manifest> diff --git a/packages/SettingsLib/IllustrationPreference/res/values/strings.xml b/packages/SettingsLib/IllustrationPreference/res/values/strings.xml new file mode 100644 index 000000000000..3a8aaf8b5092 --- /dev/null +++ b/packages/SettingsLib/IllustrationPreference/res/values/strings.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2025 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. + --> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Label for an accessibility action that starts an animation [CHAR LIMIT=30] --> + <string name="settingslib_action_label_resume">resume</string> + <!-- Label for an accessibility action that stops an animation [CHAR LIMIT=30] --> + <string name="settingslib_action_label_pause">pause</string> + <!-- Label for an accessibility action that stops an animation [CHAR LIMIT=50] --> + <string name="settingslib_state_animation_playing">Animation playing</string> + <!-- Label for an accessibility action that stops an animation [CHAR LIMIT=50] --> + <string name="settingslib_state_animation_paused">Animation paused</string> +</resources> diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java index af40c647e805..e818a603c5b4 100644 --- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java +++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java @@ -32,6 +32,8 @@ import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.widget.FrameLayout; import android.widget.ImageView; @@ -73,6 +75,7 @@ public class IllustrationPreference extends Preference implements GroupSectionDi private boolean mLottieDynamicColor; private CharSequence mContentDescription; private boolean mIsTablet; + private boolean mIsAnimatable; private boolean mIsAnimationPaused; /** @@ -81,6 +84,7 @@ public class IllustrationPreference extends Preference implements GroupSectionDi public interface OnBindListener { /** * Called when when {@link #onBindViewHolder(PreferenceViewHolder)} occurs. + * * @param animationView the animation view for this preference. */ void onBind(LottieAnimationView animationView); @@ -144,16 +148,6 @@ public class IllustrationPreference extends Preference implements GroupSectionDi (FrameLayout) holder.findViewById(R.id.middleground_layout); final LottieAnimationView illustrationView = (LottieAnimationView) holder.findViewById(R.id.lottie_view); - // Pause and resume animation - illustrationFrame.setOnClickListener(v -> { - mIsAnimationPaused = !mIsAnimationPaused; - if (mIsAnimationPaused) { - illustrationView.pauseAnimation(); - } else { - illustrationView.resumeAnimation(); - } - }); - if (illustrationView != null && !TextUtils.isEmpty(mContentDescription)) { illustrationView.setContentDescription(mContentDescription); illustrationView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); @@ -171,12 +165,13 @@ public class IllustrationPreference extends Preference implements GroupSectionDi illustrationView.setCacheComposition(mCacheComposition); handleImageWithAnimation(illustrationView, illustrationFrame); + handleAnimationControl(illustrationView, illustrationFrame); handleImageFrameMaxHeight(backgroundView, illustrationView); if (mIsAutoScale) { illustrationView.setScaleType(mIsAutoScale - ? ImageView.ScaleType.CENTER_CROP - : ImageView.ScaleType.CENTER_INSIDE); + ? ImageView.ScaleType.CENTER_CROP + : ImageView.ScaleType.CENTER_INSIDE); } handleMiddleGroundView(middleGroundLayout); @@ -377,6 +372,7 @@ public class IllustrationPreference extends Preference implements GroupSectionDi final Drawable drawable = illustrationView.getDrawable(); if (drawable != null) { startAnimation(drawable); + mIsAnimatable = false; } } @@ -386,10 +382,12 @@ public class IllustrationPreference extends Preference implements GroupSectionDi final Drawable drawable = illustrationView.getDrawable(); if (drawable != null) { startAnimation(drawable); + mIsAnimatable = false; } else { // The lottie image from the raw folder also returns null because the ImageView // couldn't handle it now. startLottieAnimationWith(illustrationView, mImageUri); + mIsAnimatable = true; } } @@ -418,10 +416,12 @@ public class IllustrationPreference extends Preference implements GroupSectionDi final Drawable drawable = illustrationView.getDrawable(); if (drawable != null) { startAnimation(drawable); + mIsAnimatable = false; } else { // The lottie image from the raw folder also returns null because the ImageView // couldn't handle it now. startLottieAnimationWith(illustrationView, mImageResId); + mIsAnimatable = true; } } } @@ -459,6 +459,60 @@ public class IllustrationPreference extends Preference implements GroupSectionDi ((Animatable) drawable).start(); } + private void handleAnimationControl(LottieAnimationView illustrationView, + ViewGroup container) { + if (mIsAnimatable) { + // TODO(b/397340540): list out pages having illustration without a content description. + if (TextUtils.isEmpty(mContentDescription)) { + Log.w(TAG, "Illustration should have a content description. preference key = " + + getKey()); + } + // Enable pause and resume abilities to animation only + container.setOnClickListener(v -> { + mIsAnimationPaused = !mIsAnimationPaused; + if (mIsAnimationPaused) { + illustrationView.pauseAnimation(); + } else { + illustrationView.resumeAnimation(); + } + updateAccessibilityAction(container); + }); + + updateAccessibilityAction(container); + } + } + + private void updateAccessibilityAction(ViewGroup container) { + // Setting the state of animation + container.setStateDescription(getStateDescriptionForAnimation()); + container.setAccessibilityDelegate(new View.AccessibilityDelegate() { + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(host, info); + final AccessibilityAction clickAction = new AccessibilityAction( + AccessibilityNodeInfo.ACTION_CLICK, + getActionLabelForAnimation()); + info.addAction(clickAction); + } + }); + } + + private String getActionLabelForAnimation() { + if (mIsAnimationPaused) { + return getContext().getString(R.string.settingslib_action_label_resume); + } else { + return getContext().getString(R.string.settingslib_action_label_pause); + } + } + + private String getStateDescriptionForAnimation() { + if (mIsAnimationPaused) { + return getContext().getString(R.string.settingslib_state_animation_paused); + } else { + return getContext().getString(R.string.settingslib_state_animation_playing); + } + } + private static void startLottieAnimationWith(LottieAnimationView illustrationView, Uri imageUri) { final InputStream inputStream = @@ -514,15 +568,20 @@ public class IllustrationPreference extends Preference implements GroupSectionDi mIsAutoScale = false; if (attrs != null) { TypedArray a = context.obtainStyledAttributes(attrs, - com.airbnb.lottie.R.styleable.LottieAnimationView, 0 /*defStyleAttr*/, 0 /*defStyleRes*/); - mImageResId = a.getResourceId(com.airbnb.lottie.R.styleable.LottieAnimationView_lottie_rawRes, 0); + com.airbnb.lottie.R.styleable.LottieAnimationView, /* defStyleAttr= */ 0, + /* defStyleRes= */ 0); + mImageResId = a.getResourceId( + com.airbnb.lottie.R.styleable.LottieAnimationView_lottie_rawRes, + /* defValue= */ 0); mCacheComposition = a.getBoolean( - com.airbnb.lottie.R.styleable.LottieAnimationView_lottie_cacheComposition, true); + com.airbnb.lottie.R.styleable.LottieAnimationView_lottie_cacheComposition, + /* defValue= */ true); a = context.obtainStyledAttributes(attrs, - R.styleable.IllustrationPreference, 0 /*defStyleAttr*/, 0 /*defStyleRes*/); + R.styleable.IllustrationPreference, /* defStyleAttr= */ 0, + /* defStyleRes= */ 0); mLottieDynamicColor = a.getBoolean(R.styleable.IllustrationPreference_dynamicColor, - false); + /* defValue= */ false); a.recycle(); } diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsThemeHelper.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsThemeHelper.kt index 1776d252e28b..1f4a48df55b0 100644 --- a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsThemeHelper.kt +++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsThemeHelper.kt @@ -50,10 +50,6 @@ object SettingsThemeHelper { } private fun tryInit(context: Context) { - if (expressiveThemeState != ExpressiveThemeState.UNKNOWN) { - return - } - expressiveThemeState = if ( (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) && diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig index d94450b1cabd..a029f56cf1d7 100644 --- a/packages/SettingsLib/aconfig/settingslib.aconfig +++ b/packages/SettingsLib/aconfig/settingslib.aconfig @@ -26,14 +26,14 @@ flag { name: "enable_le_audio_sharing" namespace: "pixel_cross_device_control" description: "Gates whether to enable LE audio sharing" - bug: "323125723" + bug: "388674074" } flag { name: "enable_le_audio_qr_code_private_broadcast_sharing" namespace: "pixel_cross_device_control" description: "Gates whether to enable LE audio private broadcast sharing via QR code" - bug: "323125723" + bug: "388674074" } flag { @@ -229,3 +229,23 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "adopt_primary_group_management_api_v2" + namespace: "cross_device_experiences" + description: "Adopt more Bluetooth LE broadcast primary group management APIs post launch" + bug: "381946931" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "promote_audio_sharing_for_second_auto_connected_lea_device" + namespace: "cross_device_experiences" + description: "Show audio sharing promote notification or dialog when the second lea device is auto connected" + bug: "395786392" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/res/res/values-w204dp-round-watch/dimens_watch.xml b/packages/SettingsLib/res/layout/activity_create_new_user.xml index 3509474c5c2e..7453b53a6956 100644 --- a/core/res/res/values-w204dp-round-watch/dimens_watch.xml +++ b/packages/SettingsLib/res/layout/activity_create_new_user.xml @@ -1,5 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- +<?xml version="1.0" encoding="utf-8"?><!-- ~ Copyright (C) 2025 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,8 +13,10 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<resources> - <!-- watch's indeterminate progress bar dimens based on the current screen size --> - <dimen name="loader_horizontal_min_width_watch">70dp</dimen> - <dimen name="loader_horizontal_min_height_watch">15dp</dimen> -</resources> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@android:color/transparent" + android:orientation="vertical"> +</LinearLayout>
\ No newline at end of file diff --git a/packages/SettingsLib/res/values/styles.xml b/packages/SettingsLib/res/values/styles.xml index 3326b6034237..7ab096fae0db 100644 --- a/packages/SettingsLib/res/values/styles.xml +++ b/packages/SettingsLib/res/values/styles.xml @@ -116,4 +116,13 @@ <item name="android:textAppearance">?android:attr/textAppearanceSmall</item> <item name="android:textSize">16dp</item> </style> + + <style name="Theme.Transparent" parent="@android:style/Theme.DeviceDefault.Settings"> + <item name="android:windowActionBar">false</item> + <item name="android:windowNoTitle">true</item> + <item name="android:windowIsTranslucent">true</item> + <item name="android:windowBackground">@android:color/transparent</item> + <item name="android:windowContentOverlay">@null</item> + </style> + </resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java index 572444edea29..bf86911ee683 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java @@ -30,13 +30,11 @@ import android.util.Log; import androidx.annotation.ChecksSdkIntAtLeast; import com.android.internal.annotations.VisibleForTesting; -import com.android.settingslib.flags.Flags; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -387,7 +385,7 @@ public class CsipDeviceManager { preferredMainDevice.refresh(); hasChanged = true; } - syncAudioSharingStatusIfNeeded(preferredMainDevice); + syncAudioSharingSourceIfNeeded(preferredMainDevice); } if (hasChanged) { log("addMemberDevicesIntoMainDevice: After changed, CachedBluetoothDevice list: " @@ -401,16 +399,13 @@ public class CsipDeviceManager { return userManager != null && userManager.isManagedProfile(); } - private void syncAudioSharingStatusIfNeeded(CachedBluetoothDevice mainDevice) { + private void syncAudioSharingSourceIfNeeded(CachedBluetoothDevice mainDevice) { boolean isAudioSharingEnabled = BluetoothUtils.isAudioSharingUIAvailable(mContext); - if (isAudioSharingEnabled && mainDevice != null) { + if (isAudioSharingEnabled) { if (isWorkProfile()) { - log("addMemberDevicesIntoMainDevice: skip sync audio sharing status, work profile"); + log("addMemberDevicesIntoMainDevice: skip sync source for work profile"); return; } - Set<CachedBluetoothDevice> deviceSet = new HashSet<>(); - deviceSet.add(mainDevice); - deviceSet.addAll(mainDevice.getMemberDevice()); boolean hasBroadcastSource = BluetoothUtils.isBroadcasting(mBtManager) && BluetoothUtils.hasConnectedBroadcastSource( mainDevice, mBtManager); @@ -424,6 +419,9 @@ public class CsipDeviceManager { if (metadata != null && assistant != null) { log("addMemberDevicesIntoMainDevice: sync audio sharing source after " + "combining the top level devices."); + Set<CachedBluetoothDevice> deviceSet = new HashSet<>(); + deviceSet.add(mainDevice); + deviceSet.addAll(mainDevice.getMemberDevice()); Set<BluetoothDevice> sinksToSync = deviceSet.stream() .map(CachedBluetoothDevice::getDevice) .filter(device -> @@ -437,24 +435,8 @@ public class CsipDeviceManager { } } } - if (Flags.enableTemporaryBondDevicesUi()) { - log("addMemberDevicesIntoMainDevice: sync temp bond metadata for audio sharing " - + "sinks after combining the top level devices."); - Set<BluetoothDevice> sinksToSync = deviceSet.stream() - .map(CachedBluetoothDevice::getDevice).filter(Objects::nonNull).collect( - Collectors.toSet()); - if (sinksToSync.stream().anyMatch(BluetoothUtils::isTemporaryBondDevice)) { - for (BluetoothDevice device : sinksToSync) { - if (!BluetoothUtils.isTemporaryBondDevice(device)) { - log("addMemberDevicesIntoMainDevice: sync temp bond metadata for " - + device.getAnonymizedAddress()); - BluetoothUtils.setTemporaryBondMetadata(device); - } - } - } - } } else { - log("addMemberDevicesIntoMainDevice: skip sync audio sharing status, flag disabled"); + log("addMemberDevicesIntoMainDevice: skip sync source, flag disabled"); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java index 84156429809b..3017d79836e8 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java @@ -66,6 +66,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -84,6 +85,8 @@ import java.util.stream.Collectors; public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { public static final String ACTION_LE_AUDIO_SHARING_STATE_CHANGE = "com.android.settings.action.BLUETOOTH_LE_AUDIO_SHARING_STATE_CHANGE"; + public static final String ACTION_LE_AUDIO_SHARING_DEVICE_CONNECTED = + "com.android.settings.action.BLUETOOTH_LE_AUDIO_SHARING_DEVICE_CONNECTED"; public static final String EXTRA_LE_AUDIO_SHARING_STATE = "BLUETOOTH_LE_AUDIO_SHARING_STATE"; public static final String EXTRA_BLUETOOTH_DEVICE = "BLUETOOTH_DEVICE"; public static final String EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE = "BT_DEVICE_TO_AUTO_ADD_SOURCE"; @@ -1189,6 +1192,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { @NonNull private Map<Integer, List<BluetoothDevice>> getDeviceGroupsInBroadcast() { + if (mServiceBroadcastAssistant == null) return new HashMap<>(); boolean hysteresisModeFixEnabled = BluetoothUtils.isAudioSharingHysteresisModeFixAvailable(mContext); List<BluetoothDevice> connectedDevices = mServiceBroadcastAssistant.getConnectedDevices(); diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java index d5cfe55813ee..460c790174ab 100644 --- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java +++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java @@ -35,6 +35,7 @@ import static java.util.Objects.requireNonNull; import android.annotation.SuppressLint; import android.app.AutomaticZenRule; import android.app.NotificationManager; +import android.content.ComponentName; import android.content.Context; import android.net.Uri; import android.os.Parcel; @@ -117,6 +118,14 @@ public class ZenMode implements Parcelable { DISABLED_BY_OTHER } + /** + * Information about the owner of a {@link ZenMode}. {@link #packageName()} is + * {@link SystemZenRules#PACKAGE_ANDROID} if the mode is system-owned; it may also be + * {@code null}, but only as an artifact of very old modes. + */ + public record Owner(@Nullable String packageName, @Nullable ComponentName configurationActivity, + @Nullable ComponentName conditionProvider) { } + private final String mId; private final AutomaticZenRule mRule; private final Kind mKind; @@ -198,7 +207,7 @@ public class ZenMode implements Parcelable { } @NonNull - public AutomaticZenRule getRule() { + AutomaticZenRule getRule() { return mRule; } @@ -207,6 +216,10 @@ public class ZenMode implements Parcelable { return Strings.nullToEmpty(mRule.getName()); } + public void setName(@NonNull String name) { + mRule.setName(name); + } + @NonNull public Kind getKind() { return mKind; @@ -217,6 +230,17 @@ public class ZenMode implements Parcelable { return mStatus; } + @NonNull + public Owner getOwner() { + return new Owner(mRule.getPackageName(), mRule.getConfigurationActivity(), + mRule.getOwner()); + } + + @Nullable + public String getOwnerPackage() { + return getOwner().packageName(); + } + @AutomaticZenRule.Type public int getType() { return mRule.getType(); @@ -257,6 +281,26 @@ public class ZenMode implements Parcelable { } } + /** + * Returns the resource id of the icon for this mode. Note that this is the <em>stored</em> + * resource id, and thus can be different from the value in {@link #getIconKey()} -- in + * particular, for modes without a custom icon set, this method returns {@code 0} whereas + * {@link #getIconKey()} will return a default icon based on other mode properties. + * + * <p>Most callers are interested in {@link #getIconKey()}, unless they are editing the icon. + */ + public int getIconResId() { + return mRule.getIconResId(); + } + + /** + * Sets the resource id of the icon for this mode. + * @see #getIconResId() + */ + public void setIconResId(@DrawableRes int iconResId) { + mRule.setIconResId(iconResId); + } + /** Returns the interruption filter of the mode. */ @NotificationManager.InterruptionFilter public int getInterruptionFilter() { @@ -445,6 +489,10 @@ public class ZenMode implements Parcelable { return mStatus == Status.ENABLED_AND_ACTIVE; } + public boolean isManualInvocationAllowed() { + return mRule.isManualInvocationAllowed(); + } + public boolean isSystemOwned() { return SystemZenRules.PACKAGE_ANDROID.equals(mRule.getPackageName()); } diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModeDescriptions.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModeDescriptions.java index f5776989917e..b67339bace67 100644 --- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModeDescriptions.java +++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModeDescriptions.java @@ -70,8 +70,7 @@ public final class ZenModeDescriptions { public String getTriggerDescriptionForAccessibility(@NonNull ZenMode mode) { // Only one special case: time-based schedules, where we want to use full day names. if (mode.isSystemOwned() && mode.getType() == TYPE_SCHEDULE_TIME) { - ZenModeConfig.ScheduleInfo schedule = ZenModeConfig.tryParseScheduleConditionId( - mode.getRule().getConditionId()); + ZenModeConfig.ScheduleInfo schedule = ZenModeSchedules.getTimeSchedule(mode); if (schedule != null) { String fullDaysSummary = SystemZenRules.getDaysOfWeekFull(mContext, schedule); if (fullDaysSummary != null) { diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModeSchedules.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModeSchedules.java new file mode 100644 index 000000000000..c981652f231d --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModeSchedules.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2025 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.settingslib.notification.modes; + +import android.service.notification.ZenModeConfig; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public class ZenModeSchedules { + + /** + * Returns the {@link ZenModeConfig.ScheduleInfo} time schedule corresponding to the mode, or + * {@code null} if the mode is not time-schedule-based. + */ + @Nullable + public static ZenModeConfig.ScheduleInfo getTimeSchedule(@NonNull ZenMode mode) { + return ZenModeConfig.tryParseScheduleConditionId(mode.getRule().getConditionId()); + } + + /** + * Returns the {@link ZenModeConfig.EventInfo} calendar schedule corresponding to the mode, or + * {@code null} if the mode is not calendar-schedule-based. + */ + @Nullable + public static ZenModeConfig.EventInfo getCalendarSchedule(@NonNull ZenMode mode) { + return ZenModeConfig.tryParseEventConditionId(mode.getRule().getConditionId()); + } + + private ZenModeSchedules() { } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/users/CreateUserActivity.java b/packages/SettingsLib/src/com/android/settingslib/users/CreateUserActivity.java new file mode 100644 index 000000000000..c5e6f60e3fa6 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/users/CreateUserActivity.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2025 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.settingslib.users; + +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.view.MotionEvent; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.settingslib.R; + + +public class CreateUserActivity extends Activity { + private static final String TAG = "CreateUserActivity"; + + public static final String EXTRA_USER_NAME = "new_user_name"; + public static final String EXTRA_IS_ADMIN = "is_admin"; + public static final String EXTRA_USER_ICON_PATH = "user_icon_path"; + private static final String DIALOG_STATE_KEY = "create_user_dialog_state"; + private static final String EXTRA_CAN_CREATE_ADMIN = "can_create_admin"; + private static final String EXTRA_FILE_AUTHORITY = "file_authority"; + + private CreateUserDialogController mCreateUserDialogController; + @VisibleForTesting + Dialog mSetupUserDialog; + + + /** + * Creates intent to start CreateUserActivity + */ + public static @NonNull Intent createIntentForStart(@NonNull Context context, + boolean canCreateAdminUser, @NonNull String fileAuth) { + Intent intent = new Intent(context, CreateUserActivity.class); + intent.putExtra(EXTRA_CAN_CREATE_ADMIN, canCreateAdminUser); + intent.putExtra(EXTRA_FILE_AUTHORITY, fileAuth); + return intent; + } + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Intent intent = getIntent(); + + mCreateUserDialogController = new CreateUserDialogController( + intent.getStringExtra(EXTRA_FILE_AUTHORITY)); + setContentView(R.layout.activity_create_new_user); + if (savedInstanceState != null) { + mCreateUserDialogController.onRestoreInstanceState(savedInstanceState); + } + mSetupUserDialog = createDialog(intent.getBooleanExtra(EXTRA_CAN_CREATE_ADMIN, false)); + mSetupUserDialog.show(); + } + + @Override + protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + Bundle savedDialogState = savedInstanceState.getBundle(DIALOG_STATE_KEY); + if (savedDialogState != null && mSetupUserDialog != null) { + mSetupUserDialog.onRestoreInstanceState(savedDialogState); + } + } + + private Dialog createDialog(boolean canCreateAdminUser) { + return mCreateUserDialogController.createDialog( + this, + this::startActivity, + canCreateAdminUser, + this::setSuccessResult, + this::cancel + ); + } + + @Override + public boolean onTouchEvent(@Nullable MotionEvent event) { + onBackInvoked(); + return super.onTouchEvent(event); + } + + private void onBackInvoked() { + if (mSetupUserDialog != null) { + mSetupUserDialog.dismiss(); + } + setResult(RESULT_CANCELED); + finish(); + } + + @VisibleForTesting + void setSuccessResult(String userName, Drawable userIcon, String path, Boolean isAdmin) { + Intent intent = new Intent(this, CreateUserActivity.class); + intent.putExtra(EXTRA_USER_NAME, userName); + intent.putExtra(EXTRA_IS_ADMIN, isAdmin); + intent.putExtra(EXTRA_USER_ICON_PATH, path); + + mSetupUserDialog.dismiss(); + setResult(RESULT_OK, intent); + finish(); + } + + @VisibleForTesting + void cancel() { + mSetupUserDialog.dismiss(); + setResult(RESULT_CANCELED); + finish(); + } + + @Override + protected void onSaveInstanceState(@NonNull Bundle outState) { + if (mSetupUserDialog != null && mSetupUserDialog.isShowing()) { + outState.putBundle(DIALOG_STATE_KEY, mSetupUserDialog.onSaveInstanceState()); + } + mCreateUserDialogController.onSaveInstanceState(outState); + super.onSaveInstanceState(outState); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + mCreateUserDialogController.onActivityResult(requestCode, resultCode, data); + } + + private void startActivity(Intent intent, int requestCode) { + startActivityForResult(intent, requestCode); + mCreateUserDialogController.startingActivityForResult(); + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java b/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java index d71b337228f6..d9f1b632323c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java +++ b/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java @@ -242,7 +242,7 @@ public class CreateUserDialogController { .setMessage(messageResId) .setNegativeButtonText(R.string.cancel) .setPositiveButtonText(R.string.next); - mCustomDialogHelper.requestFocusOnTitle(); + focus(); break; case GRANT_ADMIN_DIALOG: mEditUserInfoView.setVisibility(View.GONE); @@ -255,7 +255,7 @@ public class CreateUserDialogController { .setMessage(R.string.user_grant_admin_message) .setNegativeButtonText(R.string.back) .setPositiveButtonText(R.string.next); - mCustomDialogHelper.requestFocusOnTitle(); + focus(); if (mIsAdmin == null) { mCustomDialogHelper.setButtonEnabled(false); } @@ -267,7 +267,7 @@ public class CreateUserDialogController { .setTitle(R.string.user_info_settings_title) .setNegativeButtonText(R.string.back) .setPositiveButtonText(R.string.done); - mCustomDialogHelper.requestFocusOnTitle(); + focus(); mEditUserInfoView.setVisibility(View.VISIBLE); mGrantAdminView.setVisibility(View.GONE); break; @@ -282,7 +282,7 @@ public class CreateUserDialogController { mCustomDialogHelper.getDialog().dismiss(); break; case EXIT_DIALOG: - mCustomDialogHelper.getDialog().dismiss(); + finish(); break; default: if (mCurrentState < EXIT_DIALOG) { @@ -394,13 +394,21 @@ public class CreateUserDialogController { return mCustomDialogHelper != null && mCustomDialogHelper.getDialog() != null; } + void focus() { + mCustomDialogHelper.requestFocusOnTitle(); + } + /** * Runs callback and clears saved values after dialog is dismissed. */ public void finish() { if (mCurrentState == CREATE_USER_AND_CLOSE) { if (mSuccessCallback != null) { - mSuccessCallback.onSuccess(mUserName, mNewUserIcon, Boolean.TRUE.equals(mIsAdmin)); + if (mEditUserPhotoController != null && mCachedDrawablePath == null) { + mCachedDrawablePath = mEditUserPhotoController.getCachedDrawablePath(); + } + mSuccessCallback.onSuccess(mUserName, mNewUserIcon, mCachedDrawablePath, + Boolean.TRUE.equals(mIsAdmin)); } } else { if (mCancelCallback != null) { diff --git a/packages/SettingsLib/src/com/android/settingslib/users/NewUserData.java b/packages/SettingsLib/src/com/android/settingslib/users/NewUserData.java index 3d18b59258b3..eed608e98cc9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/users/NewUserData.java +++ b/packages/SettingsLib/src/com/android/settingslib/users/NewUserData.java @@ -18,6 +18,8 @@ package com.android.settingslib.users; import android.graphics.drawable.Drawable; +import androidx.annotation.Nullable; + /** * Defines a callback when a new user data is filled out. */ @@ -27,8 +29,10 @@ public interface NewUserData { * Consumes data relevant to new user that needs to be created. * @param userName New user name. * @param userImage New user icon. + * @param iconPath New user icon path. * @param isNewUserAdmin A boolean that indicated whether new user has admin status. */ - void onSuccess(String userName, Drawable userImage, Boolean isNewUserAdmin); + void onSuccess(@Nullable String userName, @Nullable Drawable userImage, + @Nullable String iconPath, @Nullable Boolean isNewUserAdmin); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java index 2eccaa626f3b..fd14d1ff6786 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java @@ -40,8 +40,6 @@ import android.content.Context; import android.os.Looper; import android.os.Parcel; import android.os.UserManager; -import android.platform.test.annotations.DisableFlags; -import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import com.android.settingslib.flags.Flags; @@ -76,9 +74,6 @@ public class CsipDeviceManagerTest { private final static String DEVICE_ADDRESS_1 = "AA:BB:CC:DD:EE:11"; private final static String DEVICE_ADDRESS_2 = "AA:BB:CC:DD:EE:22"; private final static String DEVICE_ADDRESS_3 = "AA:BB:CC:DD:EE:33"; - private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25; - private static final String TEMP_BOND_METADATA = - "<TEMP_BOND_TYPE>le_audio_sharing</TEMP_BOND_TYPE>"; private final static int GROUP1 = 1; private final BluetoothClass DEVICE_CLASS_1 = createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES); @@ -342,7 +337,6 @@ public class CsipDeviceManagerTest { } @Test - @DisableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING) public void addMemberDevicesIntoMainDevice_preferredDeviceIsMainAndTwoMain_returnTrue() { // Condition: The preferredDevice is main and there is another main device in top list // Expected Result: return true and there is the preferredDevice in top list @@ -352,6 +346,7 @@ public class CsipDeviceManagerTest { mCachedDevices.add(preferredDevice); mCachedDevices.add(mCachedDevice2); mCachedDevices.add(mCachedDevice3); + mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); assertThat(mCsipDeviceManager.addMemberDevicesIntoMainDevice(GROUP1, preferredDevice)) .isTrue(); @@ -364,7 +359,6 @@ public class CsipDeviceManagerTest { } @Test - @EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI}) public void addMemberDevicesIntoMainDevice_preferredDeviceIsMainAndTwoMain_workProfile_doNothing() { // Condition: The preferredDevice is main and there is another main device in top list @@ -375,6 +369,7 @@ public class CsipDeviceManagerTest { mCachedDevices.add(preferredDevice); mCachedDevices.add(mCachedDevice2); mCachedDevices.add(mCachedDevice3); + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); when(mBroadcast.isEnabled(null)).thenReturn(true); BluetoothLeBroadcastMetadata metadata = Mockito.mock(BluetoothLeBroadcastMetadata.class); when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(metadata); @@ -382,8 +377,6 @@ public class CsipDeviceManagerTest { BluetoothLeBroadcastReceiveState.class); when(state.getBisSyncState()).thenReturn(ImmutableList.of(1L)); when(mAssistant.getAllSources(mDevice2)).thenReturn(ImmutableList.of(state)); - when(mDevice2.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS)) - .thenReturn(TEMP_BOND_METADATA.getBytes()); when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager); when(mUserManager.isManagedProfile()).thenReturn(true); @@ -394,13 +387,10 @@ public class CsipDeviceManagerTest { assertThat(mCachedDevices.contains(mCachedDevice3)).isTrue(); assertThat(preferredDevice.getMemberDevice()).contains(mCachedDevice2); verify(mAssistant, never()).addSource(mDevice1, metadata, /* isGroupOp= */ false); - verify(mDevice1, never()).setMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS, - TEMP_BOND_METADATA.getBytes()); } @Test - @EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI}) - public void addMemberDevicesIntoMainDevice_preferredDeviceIsMainAndTwoMain_syncState() { + public void addMemberDevicesIntoMainDevice_preferredDeviceIsMainAndTwoMain_syncSource() { // Condition: The preferredDevice is main and there is another main device in top list // Expected Result: return true and there is the preferredDevice in top list CachedBluetoothDevice preferredDevice = mCachedDevice1; @@ -409,6 +399,7 @@ public class CsipDeviceManagerTest { mCachedDevices.add(preferredDevice); mCachedDevices.add(mCachedDevice2); mCachedDevices.add(mCachedDevice3); + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); when(mBroadcast.isEnabled(null)).thenReturn(true); BluetoothLeBroadcastMetadata metadata = Mockito.mock(BluetoothLeBroadcastMetadata.class); when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(metadata); @@ -416,8 +407,6 @@ public class CsipDeviceManagerTest { BluetoothLeBroadcastReceiveState.class); when(state.getBisSyncState()).thenReturn(ImmutableList.of(1L)); when(mAssistant.getAllSources(mDevice2)).thenReturn(ImmutableList.of(state)); - when(mDevice2.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS)) - .thenReturn(TEMP_BOND_METADATA.getBytes()); assertThat(mCsipDeviceManager.addMemberDevicesIntoMainDevice(GROUP1, preferredDevice)) .isTrue(); @@ -426,8 +415,6 @@ public class CsipDeviceManagerTest { assertThat(mCachedDevices.contains(mCachedDevice3)).isTrue(); assertThat(preferredDevice.getMemberDevice()).contains(mCachedDevice2); verify(mAssistant).addSource(mDevice1, metadata, /* isGroupOp= */ false); - verify(mDevice1).setMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS, - TEMP_BOND_METADATA.getBytes()); } @Test @@ -449,13 +436,13 @@ public class CsipDeviceManagerTest { } @Test - @EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI}) public void addMemberDevicesIntoMainDevice_preferredDeviceIsMemberAndTwoMain_returnTrue() { // Condition: The preferredDevice is member and there are two main device in top list // Expected Result: return true and there is the preferredDevice in top list CachedBluetoothDevice preferredDevice = mCachedDevice2; BluetoothDevice expectedMainBluetoothDevice = preferredDevice.getDevice(); mCachedDevice3.setGroupId(GROUP1); + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); when(mBroadcast.isEnabled(null)).thenReturn(false); assertThat(mCsipDeviceManager.addMemberDevicesIntoMainDevice(GROUP1, preferredDevice)) @@ -470,20 +457,16 @@ public class CsipDeviceManagerTest { assertThat(mCachedDevice1.getDevice()).isEqualTo(expectedMainBluetoothDevice); verify(mAssistant, never()).addSource(any(BluetoothDevice.class), any(BluetoothLeBroadcastMetadata.class), anyBoolean()); - verify(mDevice2, never()).setMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS, - TEMP_BOND_METADATA.getBytes()); - verify(mDevice3, never()).setMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS, - TEMP_BOND_METADATA.getBytes()); } @Test - @EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI}) - public void addMemberDevicesIntoMainDevice_preferredDeviceIsMemberAndTwoMain_syncState() { + public void addMemberDevicesIntoMainDevice_preferredDeviceIsMemberAndTwoMain_syncSource() { // Condition: The preferredDevice is member and there are two main device in top list // Expected Result: return true and there is the preferredDevice in top list CachedBluetoothDevice preferredDevice = mCachedDevice2; BluetoothDevice expectedMainBluetoothDevice = preferredDevice.getDevice(); mCachedDevice3.setGroupId(GROUP1); + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); when(mBroadcast.isEnabled(null)).thenReturn(true); BluetoothLeBroadcastMetadata metadata = Mockito.mock(BluetoothLeBroadcastMetadata.class); when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(metadata); @@ -491,8 +474,6 @@ public class CsipDeviceManagerTest { BluetoothLeBroadcastReceiveState.class); when(state.getBisSyncState()).thenReturn(ImmutableList.of(1L)); when(mAssistant.getAllSources(mDevice1)).thenReturn(ImmutableList.of(state)); - when(mDevice1.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS)) - .thenReturn(TEMP_BOND_METADATA.getBytes()); assertThat(mCsipDeviceManager.addMemberDevicesIntoMainDevice(GROUP1, preferredDevice)) .isTrue(); @@ -507,10 +488,6 @@ public class CsipDeviceManagerTest { assertThat(mCachedDevice1.getDevice()).isEqualTo(expectedMainBluetoothDevice); verify(mAssistant).addSource(mDevice2, metadata, /* isGroupOp= */ false); verify(mAssistant).addSource(mDevice3, metadata, /* isGroupOp= */ false); - verify(mDevice2).setMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS, - TEMP_BOND_METADATA.getBytes()); - verify(mDevice3).setMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS, - TEMP_BOND_METADATA.getBytes()); } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java index 6b30f159129e..71ecf5b76296 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java @@ -21,6 +21,7 @@ import static android.app.AutomaticZenRule.TYPE_DRIVING; import static android.app.AutomaticZenRule.TYPE_IMMERSIVE; import static android.app.AutomaticZenRule.TYPE_OTHER; import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR; +import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME; import static android.app.AutomaticZenRule.TYPE_THEATER; import static android.app.AutomaticZenRule.TYPE_UNKNOWN; import static android.app.NotificationManager.INTERRUPTION_FILTER_ALARMS; @@ -35,6 +36,8 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertThrows; import android.app.AutomaticZenRule; +import android.content.ComponentName; +import android.content.Context; import android.net.Uri; import android.os.Parcel; import android.service.notification.Condition; @@ -43,13 +46,17 @@ import android.service.notification.ZenDeviceEffects; import android.service.notification.ZenModeConfig; import android.service.notification.ZenPolicy; +import androidx.test.core.app.ApplicationProvider; + import com.android.internal.R; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import java.util.ArrayList; +import java.util.Calendar; import java.util.List; @RunWith(RobolectricTestRunner.class) @@ -72,6 +79,13 @@ public class ZenModeTest { .setType(TYPE_OTHER) .build(); + private Context mContext; + + @Before + public void setUp() { + mContext = ApplicationProvider.getApplicationContext(); + } + @Test public void testBasicMethods_mode() { ZenMode zenMode = new ZenMode("id", ZEN_RULE, zenConfigRuleFor(ZEN_RULE, true)); @@ -95,7 +109,7 @@ public class ZenModeTest { assertThat(manualMode.canEditPolicy()).isTrue(); assertThat(manualMode.canBeDeleted()).isFalse(); assertThat(manualMode.isActive()).isFalse(); - assertThat(manualMode.getRule().getPackageName()).isEqualTo(PACKAGE_ANDROID); + assertThat(manualMode.getOwnerPackage()).isEqualTo(PACKAGE_ANDROID); } @Test @@ -153,7 +167,7 @@ public class ZenModeTest { public void isCustomManual_scheduleTime_false() { AutomaticZenRule rule = new AutomaticZenRule.Builder("Mode", Uri.parse("x")) .setPackage(SystemZenRules.PACKAGE_ANDROID) - .setType(AutomaticZenRule.TYPE_SCHEDULE_TIME) + .setType(TYPE_SCHEDULE_TIME) .build(); ZenMode mode = new ZenMode("id", rule, zenConfigRuleFor(rule, false)); @@ -194,6 +208,23 @@ public class ZenModeTest { } @Test + public void getOwner_returnsOwnerDetails() { + AutomaticZenRule azr = new AutomaticZenRule.Builder("Rule", Uri.EMPTY) + .setPackage("package") + .setConfigurationActivity(new ComponentName("package", "configActivity")) + .setOwner(new ComponentName("package", "conditionService")) + .build(); + ZenMode zenMode = new ZenMode("id", azr, zenConfigRuleFor(azr, false)); + + ZenMode.Owner owner = zenMode.getOwner(); + assertThat(owner.packageName()).isEqualTo("package"); + assertThat(owner.configurationActivity()).isEqualTo( + new ComponentName("package", "configActivity")); + assertThat(owner.conditionProvider()).isEqualTo( + new ComponentName("package", "conditionService")); + } + + @Test public void getPolicy_interruptionFilterPriority_returnsZenPolicy() { AutomaticZenRule azr = new AutomaticZenRule.Builder("Rule", Uri.EMPTY) .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) @@ -245,7 +276,7 @@ public class ZenModeTest { zenMode.setPolicy(ZEN_POLICY); - assertThat(zenMode.getRule().getInterruptionFilter()).isEqualTo( + assertThat(zenMode.getInterruptionFilter()).isEqualTo( INTERRUPTION_FILTER_PRIORITY); assertThat(zenMode.getPolicy()).isEqualTo(ZEN_POLICY); assertThat(zenMode.getRule().getZenPolicy()).isEqualTo(ZEN_POLICY); @@ -397,6 +428,7 @@ public class ZenModeTest { assertThat(iconKey.resPackage()).isEqualTo("some.package"); assertThat(iconKey.resId()).isEqualTo(123); + assertThat(mode.getIconResId()).isEqualTo(123); } @Test @@ -411,6 +443,7 @@ public class ZenModeTest { assertThat(iconKey.resPackage()).isNull(); assertThat(iconKey.resId()).isEqualTo(123); + assertThat(mode.getIconResId()).isEqualTo(123); } @Test @@ -425,15 +458,18 @@ public class ZenModeTest { assertThat(iconKey.resPackage()).isEqualTo("some.package"); assertThat(iconKey.resId()).isEqualTo(123); + assertThat(mode.getIconResId()).isEqualTo(123); } @Test public void getIconKey_manualDnd_isDndIcon() { - ZenIcon.Key iconKey = TestModeBuilder.MANUAL_DND.getIconKey(); + ZenMode mode = TestModeBuilder.MANUAL_DND; + ZenIcon.Key iconKey = mode.getIconKey(); assertThat(iconKey.resPackage()).isNull(); assertThat(iconKey.resId()).isEqualTo( com.android.internal.R.drawable.ic_zen_mode_type_special_dnd); + assertThat(mode.getIconResId()).isEqualTo(0); } @Test @@ -448,6 +484,7 @@ public class ZenModeTest { assertThat(iconKey.resPackage()).isNull(); assertThat(iconKey.resId()).isEqualTo( com.android.internal.R.drawable.ic_zen_mode_type_bedtime); + assertThat(mode.getIconResId()).isEqualTo(0); } @Test @@ -462,6 +499,7 @@ public class ZenModeTest { assertThat(iconKey.resPackage()).isNull(); assertThat(iconKey.resId()).isEqualTo( com.android.internal.R.drawable.ic_zen_mode_type_schedule_calendar); + assertThat(mode.getIconResId()).isEqualTo(0); } @Test @@ -477,6 +515,77 @@ public class ZenModeTest { assertThat(iconKey.resPackage()).isNull(); assertThat(iconKey.resId()).isEqualTo( com.android.internal.R.drawable.ic_zen_mode_type_special_dnd); + assertThat(mode.getIconResId()).isEqualTo(0); + } + + @Test + public void setCustomModeConditionId_timeSchedule() { + ZenMode mode = new TestModeBuilder() + .setPackage(PACKAGE_ANDROID) + .build(); + ZenModeConfig.ScheduleInfo timeSchedule = new ZenModeConfig.ScheduleInfo(); + timeSchedule.startHour = 9; + timeSchedule.endHour = 12; + timeSchedule.days = new int[] {Calendar.SATURDAY, Calendar.SUNDAY}; + Uri scheduleUri = ZenModeConfig.toScheduleConditionId(timeSchedule); + + mode.setCustomModeConditionId(mContext, scheduleUri); + + assertThat(mode.getType()).isEqualTo(TYPE_SCHEDULE_TIME); + assertThat(ZenModeSchedules.getTimeSchedule(mode)).isEqualTo(timeSchedule); + assertThat(mode.getTriggerDescription()).isEqualTo("Sun, Sat, 9:00 AM - 12:00 PM"); + + assertThat(mode.getRule().getConditionId()).isEqualTo(scheduleUri); + assertThat(mode.getRule().getOwner()).isEqualTo( + ZenModeConfig.getScheduleConditionProvider()); + } + + @Test + public void setCustomModeConditionId_calendarSchedule() { + ZenMode mode = new TestModeBuilder() + .setPackage(PACKAGE_ANDROID) + .build(); + ZenModeConfig.EventInfo calendarSchedule = new ZenModeConfig.EventInfo(); + calendarSchedule.calendarId = 1L; + calendarSchedule.calName = "My events"; + Uri scheduleUri = ZenModeConfig.toEventConditionId(calendarSchedule); + + mode.setCustomModeConditionId(mContext, scheduleUri); + + assertThat(mode.getType()).isEqualTo(TYPE_SCHEDULE_CALENDAR); + assertThat(ZenModeSchedules.getCalendarSchedule(mode)).isEqualTo(calendarSchedule); + assertThat(mode.getTriggerDescription()).isEqualTo("My events"); + + assertThat(mode.getRule().getConditionId()).isEqualTo(scheduleUri); + assertThat(mode.getRule().getOwner()).isEqualTo( + ZenModeConfig.getEventConditionProvider()); + } + + @Test + public void setCustomModeConditionId_manualSchedule() { + ZenMode mode = new TestModeBuilder() + .setPackage(PACKAGE_ANDROID) + .build(); + + mode.setCustomModeConditionId(mContext, ZenModeConfig.toCustomManualConditionId()); + + assertThat(mode.getType()).isEqualTo(TYPE_OTHER); + assertThat(mode.getTriggerDescription()).isEqualTo(""); + + assertThat(mode.getRule().getConditionId()).isEqualTo( + ZenModeConfig.toCustomManualConditionId()); + assertThat(mode.getRule().getOwner()).isEqualTo( + ZenModeConfig.getCustomManualConditionProvider()); + } + + @Test + public void setCustomModeConditionId_nonSystemRule_throws() { + ZenMode mode = new TestModeBuilder() + .setPackage("some.other.package") + .build(); + + assertThrows(IllegalStateException.class, + () -> mode.setCustomModeConditionId(mContext, Uri.parse("blah"))); } private static void assertUnparceledIsEqualToOriginal(String type, ZenMode original) { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserActivityTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserActivityTest.java new file mode 100644 index 000000000000..f58eb7cc2e31 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserActivityTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2025 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.settingslib.users; + +import static com.android.settingslib.users.CreateUserActivity.EXTRA_IS_ADMIN; +import static com.android.settingslib.users.CreateUserActivity.EXTRA_USER_ICON_PATH; +import static com.android.settingslib.users.CreateUserActivity.EXTRA_USER_NAME; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.robolectric.Shadows.shadowOf; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.view.MotionEvent; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class CreateUserActivityTest { + + private static final String TEST_USER_NAME = "test_user"; + private static final String TEST_USER_ICON_PATH = "/test_path"; + private static final boolean TEST_IS_ADMIN = true; + + private Context mContext; + private CreateUserActivity mCreateUserActivity; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mCreateUserActivity = Robolectric.buildActivity(CreateUserActivity.class).setup().get(); + } + + @Test + public void startActivity_startsActivityForResult() { + Intent activityIntent = CreateUserActivity.createIntentForStart(mContext, true, ""); + mCreateUserActivity.startActivity(activityIntent, null); + + assertThat(shadowOf(mCreateUserActivity).getNextStartedActivityForResult().intent) + .isEqualTo(activityIntent); + } + + @Test + public void onTouchEvent_dismissesDialogAndCancelsResult() { + mCreateUserActivity.onTouchEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, + 0)); + + assertThat(mCreateUserActivity.mSetupUserDialog.isShowing()).isFalse(); + assertThat(shadowOf(mCreateUserActivity).getResultCode()) + .isEqualTo(Activity.RESULT_CANCELED); + } + + @Test + public void setSuccessResult_dismissesDialogAndSetsSuccessResult() { + Drawable mockDrawable = mock(Drawable.class); + + mCreateUserActivity.setSuccessResult(TEST_USER_NAME, mockDrawable, TEST_USER_ICON_PATH, + TEST_IS_ADMIN); + + assertThat(mCreateUserActivity.mSetupUserDialog.isShowing()).isFalse(); + assertThat(shadowOf(mCreateUserActivity).getResultCode()).isEqualTo(Activity.RESULT_OK); + + Intent resultIntent = shadowOf(mCreateUserActivity).getResultIntent(); + assertThat(resultIntent.getStringExtra(EXTRA_USER_NAME)).isEqualTo(TEST_USER_NAME); + assertThat(resultIntent.getBooleanExtra(EXTRA_IS_ADMIN, false)).isEqualTo(TEST_IS_ADMIN); + assertThat(resultIntent.getStringExtra(EXTRA_USER_ICON_PATH)) + .isEqualTo(TEST_USER_ICON_PATH); + } + + @Test + public void cancel_dismissesDialogAndSetsCancelResult() { + mCreateUserActivity.cancel(); + + assertThat(mCreateUserActivity.mSetupUserDialog.isShowing()).isFalse(); + assertThat(shadowOf(mCreateUserActivity).getResultCode()) + .isEqualTo(Activity.RESULT_CANCELED); + } + + @Test + public void onSaveInstanceState_savesDialogState() { + Bundle outState = new Bundle(); + mCreateUserActivity.onSaveInstanceState(outState); + + CreateUserActivity restoredActivity = + Robolectric.buildActivity(CreateUserActivity.class).setup(outState).get(); + + assertThat(restoredActivity.mSetupUserDialog).isNotNull(); + assertThat(restoredActivity.mSetupUserDialog.isShowing()).isTrue(); + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java index 68312223b4b1..e60232339e4c 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java @@ -211,7 +211,7 @@ public class CreateUserDialogControllerTest { editText.setText(expectedNewName); next.performClick(); verify(successCallback, times(1)) - .onSuccess(expectedNewName, null, true); + .onSuccess(expectedNewName, null, null, true); verifyNoInteractions(cancelCallback); } @@ -233,7 +233,7 @@ public class CreateUserDialogControllerTest { editText.setText(expectedNewName); next.performClick(); verify(successCallback, times(1)) - .onSuccess(expectedNewName, null, false); + .onSuccess(expectedNewName, null, null, false); verifyNoInteractions(cancelCallback); } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 8e3aa65fa5c7..bc281eea39d8 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -1503,7 +1503,7 @@ public class SettingsProvider extends ContentProvider { if (DEBUG) { Slog.v(LOG_TAG, "insertGlobalSetting(" + name + ", " + value + ", " + ", " + tag + ", " + makeDefault + ", " + requestingUserId - + ", " + forceNotify + ")"); + + ", " + forceNotify + ", " + overrideableByRestore + ")"); } return mutateGlobalSetting(name, value, tag, makeDefault, requestingUserId, MUTATION_OPERATION_INSERT, forceNotify, 0, overrideableByRestore); @@ -1785,7 +1785,7 @@ public class SettingsProvider extends ContentProvider { if (DEBUG) { Slog.v(LOG_TAG, "insertSecureSetting(" + name + ", " + value + ", " + ", " + tag + ", " + makeDefault + ", " + requestingUserId - + ", " + forceNotify + ")"); + + ", " + forceNotify + ", " + overrideableByRestore + ")"); } return mutateSecureSetting(name, value, tag, makeDefault, requestingUserId, MUTATION_OPERATION_INSERT, forceNotify, 0, overrideableByRestore); @@ -1946,7 +1946,7 @@ public class SettingsProvider extends ContentProvider { boolean overrideableByRestore) { if (DEBUG) { Slog.v(LOG_TAG, "insertSystemSetting(" + name + ", " + value + ", " - + requestingUserId + ")"); + + requestingUserId + ", " + overrideableByRestore + ")"); } return mutateSystemSetting(name, value, /* tag= */ null, requestingUserId, diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java index 17ebf6fc3235..0484defeb4d7 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java @@ -30,6 +30,7 @@ import android.os.ShellCommand; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.util.Slog; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -94,6 +95,8 @@ final public class SettingsService extends Binder { } final static class MyShellCommand extends ShellCommand { + private static final String LOG_TAG = "SettingsShellCmd"; + final SettingsProvider mProvider; final boolean mDumping; @@ -115,6 +118,7 @@ final public class SettingsService extends Binder { String mTag = null; int mResetMode = -1; boolean mMakeDefault; + boolean mOverrideableByRestore; MyShellCommand(SettingsProvider provider, boolean dumping) { mProvider = provider; @@ -209,6 +213,7 @@ final public class SettingsService extends Binder { return -1; } break; + // At this point, mVerb == PUT } else if (mKey == null) { mKey = arg; // keep going; there's another PUT arg @@ -217,36 +222,8 @@ final public class SettingsService extends Binder { // what we have so far is a valid command valid = true; // keep going; there may be another PUT arg - } else if (mTag == null) { - mTag = arg; - if ("default".equalsIgnoreCase(mTag)) { - mTag = null; - mMakeDefault = true; - if (peekNextArg() == null) { - valid = true; - } else { - perr.println("Too many arguments"); - return -1; - } - break; - } - if (peekNextArg() == null) { - valid = true; - break; - } - } else { // PUT, final arg - if (!"default".equalsIgnoreCase(arg)) { - perr.println("Argument expected to be 'default'"); - return -1; - } - mMakeDefault = true; - if (peekNextArg() == null) { - valid = true; - } else { - perr.println("Too many arguments"); - return -1; - } - break; + } else { + valid = parseOptionalPutArgument(arg); } } while ((arg = getNextArg()) != null); @@ -275,7 +252,8 @@ final public class SettingsService extends Binder { pout.println(getForUser(iprovider, mUser, mTable, mKey)); break; case PUT: - putForUser(iprovider, mUser, mTable, mKey, mValue, mTag, mMakeDefault); + putForUser(iprovider, mUser, mTable, mKey, mValue, mTag, mMakeDefault, + mOverrideableByRestore); break; case DELETE: pout.println("Deleted " @@ -297,6 +275,41 @@ final public class SettingsService extends Binder { return 0; } + private boolean parseOptionalPutArgument(String arg) { + boolean valid = true; + // Given that the order is TAG default overrideableByRestore, we need to parse from the + // opposite direction + switch (arg) { + case "overrideableByRestore": + if (mOverrideableByRestore) { + valid = false; + } else { + mOverrideableByRestore = true; + } + break; + case "default": + if (mMakeDefault || mOverrideableByRestore) { + valid = false; + } else { + mMakeDefault = true; + } + break; + default: // tag + if (mMakeDefault || mOverrideableByRestore || mTag != null) { + valid = false; + } else { + mTag = arg; + } + break; + } + if (!valid) { + Slog.e(LOG_TAG, "parseOptionalPutArgument(" + arg + "): invalid state (" + + "mTag=" + mTag + ", mMakeDefault=" + mMakeDefault + + ", mOverrideableByRestore=" + mOverrideableByRestore + ")"); + } + return valid; + } + List<String> listForUser(IContentProvider provider, int userHandle, String table) { final String callListCommand; if ("system".equals(table)) callListCommand = Settings.CALL_METHOD_LIST_SYSTEM; @@ -351,7 +364,11 @@ final public class SettingsService extends Binder { } void putForUser(IContentProvider provider, int userHandle, final String table, - final String key, final String value, String tag, boolean makeDefault) { + final String key, final String value, String tag, boolean makeDefault, + boolean overrideableByRestore) { + Slog.v(LOG_TAG, "putForUser(userId=" + userHandle + ", table=" + table + ", key=" + key + + ", value=" + value + ", tag=" + tag + ", makeDefault=" + makeDefault + + ", overrideableByRestore=" + overrideableByRestore + ")"); final String callPutCommand; if ("system".equals(table)) { callPutCommand = Settings.CALL_METHOD_PUT_SYSTEM; @@ -377,6 +394,9 @@ final public class SettingsService extends Binder { if (makeDefault) { arg.putBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY, true); } + if (overrideableByRestore) { + arg.putBoolean(Settings.CALL_METHOD_OVERRIDEABLE_BY_RESTORE_KEY, true); + } final AttributionSource attributionSource = new AttributionSource( Binder.getCallingUid(), resolveCallingPackage(), /*attributionTag*/ null); provider.call(attributionSource, Settings.AUTHORITY, @@ -474,10 +494,11 @@ final public class SettingsService extends Binder { pw.println(" Print this help text."); pw.println(" get [--user <USER_ID> | current] NAMESPACE KEY"); pw.println(" Retrieve the current value of KEY."); - pw.println(" put [--user <USER_ID> | current] NAMESPACE KEY VALUE [TAG] [default]"); + pw.println(" put [--user <USER_ID> | current] NAMESPACE KEY VALUE [TAG] [default] [overrideableByRestore]"); pw.println(" Change the contents of KEY to VALUE."); - pw.println(" TAG to associate with the setting."); + pw.println(" TAG to associate with the setting (cannot be default or overrideableByRestore)."); pw.println(" {default} to set as the default, case-insensitive only for global/secure namespace"); + pw.println(" {overrideableByRestore} to let the value be overridden by BackupManager on restore operations"); pw.println(" delete [--user <USER_ID> | current] NAMESPACE KEY"); pw.println(" Delete the entry for KEY."); pw.println(" reset [--user <USER_ID> | current] NAMESPACE {PACKAGE_NAME | RESET_MODE}"); diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt index 444389fb26ea..fdb07bdbe7f3 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt @@ -155,7 +155,7 @@ constructor( /** [ViewTransitionToken] to be used for storing transitioning view in [transitionRegistry] */ private val transitionToken = if (Flags.decoupleViewControllerInAnimlib()) { - ViewTransitionToken(transitioningView::class.java) + transitionRegistry?.register(transitioningView) } else { null } @@ -164,7 +164,7 @@ constructor( private val ghostedView: View get() = if (Flags.decoupleViewControllerInAnimlib()) { - transitionRegistry?.getView(transitionToken!!) + transitionToken?.let { token -> transitionRegistry?.getView(token) } } else { _ghostedView }!! @@ -186,10 +186,6 @@ constructor( ) } - if (Flags.decoupleViewControllerInAnimlib()) { - transitionRegistry?.register(transitionToken!!, transitioningView) - } - /** Find the first view with a background in [view] and its children. */ fun findBackground(view: View): Drawable? { if (view.background != null) { diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/IViewTransitionRegistry.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/IViewTransitionRegistry.kt index af3ca87bf788..280d90de7ace 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/IViewTransitionRegistry.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/IViewTransitionRegistry.kt @@ -22,12 +22,12 @@ import android.view.View interface IViewTransitionRegistry { /** - * Registers the transitioning [view] mapped to a [token] + * Registers the transitioning [view] mapped to returned token * - * @param token The token corresponding to the transitioning view * @param view The view undergoing transition + * @return token mapped to the transitioning view */ - fun register(token: ViewTransitionToken, view: View) + fun register(view: View): ViewTransitionToken /** * Unregisters the transitioned view from its corresponding [token] diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewTransitionRegistry.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewTransitionRegistry.kt index 86c7f76c6bee..882ff3b61ba9 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewTransitionRegistry.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewTransitionRegistry.kt @@ -22,21 +22,21 @@ import java.lang.ref.WeakReference /** * A registry to temporarily store the view being transitioned into a Dialog (using - * [DialogTransitionAnimator]) or an Activity (using [ActivityTransitionAnimator]) + * [DialogTransitionAnimator]) or an Activity (using [ActivityTransitionAnimator]). */ class ViewTransitionRegistry : IViewTransitionRegistry { /** * A map of a unique token to a WeakReference of the View being transitioned. WeakReference * ensures that Views are garbage collected whenever they become eligible and avoid any - * memory leaks + * memory leaks. */ - private val registry by lazy { mutableMapOf<ViewTransitionToken, WeakReference<View>>() } + private val registry by lazy { mutableMapOf<ViewTransitionToken, ViewTransitionInfo>() } /** * A [View.OnAttachStateChangeListener] to be attached to all views stored in the registry to * ensure that views (and their corresponding entry) is automatically removed when the view is - * detached from the Window + * detached from the Window. */ private val listener by lazy { object : View.OnAttachStateChangeListener { @@ -45,74 +45,121 @@ class ViewTransitionRegistry : IViewTransitionRegistry { } override fun onViewDetachedFromWindow(view: View) { - getViewToken(view)?.let { token -> unregister(token) } + // if view is detached from window, remove it from registry irrespective of number + // of reference held by clients/user of this registry + getViewToken(view)?.let { token -> remove(token) } } } } /** - * Creates an entry of a unique "token" mapped to "transitioning view" in the registry + * Creates an entry of a unique token mapped to transitioning [view] in the registry. * - * @param token unique token associated with the transitioning view * @param view view undergoing transitions + * @return unique token mapped to the view being registered */ - override fun register(token: ViewTransitionToken, view: View) { + override fun register(view: View): ViewTransitionToken { + // if view being registered is already present in the registry and has a unique token + // assigned to it, reuse that token + getViewToken(view)?.let { token -> + registry[token]?.let { info -> info.viewRefCount += 1 } + return token + } + // token embedded as a view tag enables to use a single listener for all views + val token = ViewTransitionToken(view::class.java) view.setTag(R.id.tag_view_transition_token, token) view.addOnAttachStateChangeListener(listener) - registry[token] = WeakReference(view) + registry[token] = ViewTransitionInfo(WeakReference(view)) onRegistryUpdate() + + return token } /** - * Removes the entry associated with the unique "token" in the registry + * Unregisters a view mapped to the unique [token] in the registry. This will either remove the + * entry entirely from registry (if the reference count of the associated view reached zero) or + * will decrement the reference count of the associated view in the registry. * * @param token unique token associated with the transitioning view */ override fun unregister(token: ViewTransitionToken) { - registry.remove(token)?.let { - it.get()?.let { view -> + registry[token]?.let { info -> + info.viewRefCount -= 1 + if (info.viewRefCount == 0) { + remove(token) + } + } + } + + /** + * Removes the entry associated with the unique [token] in the registry. + * + * @param token unique token associated with the transitioning view + */ + private fun remove(token: ViewTransitionToken) { + registry.remove(token)?.let { removedInfo -> + removedInfo.viewRef.get()?.let { view -> view.removeOnAttachStateChangeListener(listener) view.setTag(R.id.tag_view_transition_token, null) } - it.clear() + removedInfo.viewRef.clear() onRegistryUpdate() } } /** - * Access a view from registry using unique "token" associated with it + * Access a view from registry using unique [token] associated with it. * WARNING - this returns a StrongReference to the View stored in the registry */ override fun getView(token: ViewTransitionToken): View? { - return registry[token]?.get() + return registry[token]?.viewRef?.get() } /** - * Return token mapped to the [view], if it is present in the registry + * Return token mapped to the [view], if it is present in the registry. * * @param view the transitioning view whose token we are requesting * @return token associated with the [view] if present, else null */ override fun getViewToken(view: View): ViewTransitionToken? { - return (view.getTag(R.id.tag_view_transition_token) as? ViewTransitionToken)?.let { token -> - getView(token)?.let { token } + // extract token from the view if it is embedded inside it as a tag + val token = view.getTag(R.id.tag_view_transition_token) as? ViewTransitionToken + + // this should never really happen, but if token embedded inside the view as tag, doesn't + // point to a valid view in the registry, remove that token (tag) from the view and registry + if (token != null && getView(token) == null) { + view.setTag(R.id.tag_view_transition_token, null) + remove(token) + return null } + + return token } - /** Event call to run on registry update (on both [register] and [unregister]) */ + /** Event call to run on registry update (on both [register] and [unregister]). */ override fun onRegistryUpdate() { emitCountForTrace() } /** * Utility function to emit number of non-null views in the registry whenever the registry is - * updated (via [register] or [unregister]) + * updated (via [register] or [unregister]). */ private fun emitCountForTrace() { Trace.setCounter("transition_registry_view_count", registry.count().toLong()) } + /** Information associated with each transitioning view in the registry. */ + private data class ViewTransitionInfo( + + /** View being transitioned */ + val viewRef: WeakReference<View>, + + /** Count of clients (users of this registry) referencing same transitioning view */ + var viewRefCount: Int = 1 + ) + companion object { val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { ViewTransitionRegistry() } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt index 60eaa28e3822..b9aca25e1675 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt @@ -21,7 +21,6 @@ import com.android.compose.animation.scene.MutableSceneTransitionLayoutState import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.TransitionKey -import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.observableTransitionState import com.android.systemui.scene.shared.model.SceneDataSource import kotlinx.coroutines.CoroutineScope @@ -106,6 +105,6 @@ class SceneTransitionLayoutDataSource( } override fun freezeAndAnimateToCurrentState() { - (state.transitionState as? TransitionState.Transition)?.freezeAndAnimateToCurrentState() + state.currentTransition?.freezeAndAnimateToCurrentState() } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt index 052d520ac92f..18b68d2fa8a3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt @@ -153,10 +153,12 @@ class GhostedViewTransitionAnimatorControllerTest : SysuiTestCase() { private class FakeViewTransitionRegistry : IViewTransitionRegistry { val registry = mutableMapOf<ViewTransitionToken, View>() + val token = ViewTransitionToken() - override fun register(token: ViewTransitionToken, view: View) { + override fun register(view: View): ViewTransitionToken { registry[token] = view view.setTag(R.id.tag_view_transition_token, token) + return token } override fun unregister(token: ViewTransitionToken) { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/ViewTransitionRegistryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/ViewTransitionRegistryTest.kt index ef91c793a2f3..b18eafd206ca 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/ViewTransitionRegistryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/ViewTransitionRegistryTest.kt @@ -25,9 +25,9 @@ import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.runner.RunWith import org.mockito.kotlin.argumentCaptor -import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever import kotlin.test.Test @SmallTest @@ -36,24 +36,22 @@ class ViewTransitionRegistryTest : SysuiTestCase() { private lateinit var view: View private lateinit var underTest: ViewTransitionRegistry - private var token: ViewTransitionToken = ViewTransitionToken() @Before fun setup() { view = FrameLayout(mContext) underTest = ViewTransitionRegistry() - token = ViewTransitionToken() } @Test fun testSuccessfulRegisterInViewTransitionRegistry() { - underTest.register(token, view) + val token = underTest.register(view) assertThat(underTest.getView(token)).isNotNull() } @Test fun testSuccessfulUnregisterInViewTransitionRegistry() { - underTest.register(token, view) + val token = underTest.register(view) assertThat(underTest.getView(token)).isNotNull() underTest.unregister(token) @@ -62,13 +60,14 @@ class ViewTransitionRegistryTest : SysuiTestCase() { @Test fun testSuccessfulUnregisterOnViewDetachedFromWindow() { - val view: View = mock { - on { getTag(R.id.tag_view_transition_token) } doReturn token - } + val view: View = mock() - underTest.register(token, view) + val token = underTest.register(view) + assertThat(token).isEqualTo(token) assertThat(underTest.getView(token)).isNotNull() + whenever(view.getTag(R.id.tag_view_transition_token)).thenReturn(token) + argumentCaptor<View.OnAttachStateChangeListener>() .apply { verify(view).addOnAttachStateChangeListener(capture()) } .firstValue @@ -76,4 +75,58 @@ class ViewTransitionRegistryTest : SysuiTestCase() { assertThat(underTest.getView(token)).isNull() } + + @Test + fun testMultipleRegisterOnSameView() { + val token = underTest.register(view) + + // multiple register on same view should return same token + assertThat(underTest.register(view)).isEqualTo(token) + + // 1st unregister doesn't remove the token from registry as refCount = 2 + underTest.unregister(token) + assertThat(underTest.getView(token)).isNotNull() + + // 2nd unregister removes the token from registry + underTest.unregister(token) + assertThat(underTest.getView(token)).isNull() + } + + @Test + fun testMultipleRegisterOnSameViewRemovedAfterViewDetached() { + val view: View = mock() + + val token = underTest.register(view) + whenever(view.getTag(R.id.tag_view_transition_token)).thenReturn(token) + + assertThat(underTest.getViewToken(view)).isEqualTo(token) + + // mock view's detach event + val caller = argumentCaptor<View.OnAttachStateChangeListener>() + .apply { verify(view).addOnAttachStateChangeListener(capture()) } + .firstValue + + // register 3 times + underTest.register(view) + underTest.register(view) + underTest.register(view) + + // unregister 1 time and verify entry should still be present in registry + underTest.unregister(token) + assertThat(underTest.getView(token)).isNotNull() + + // view's associated entry should be gone from registry, after view detaches + caller.onViewDetachedFromWindow(view) + assertThat(underTest.getView(token)).isNull() + } + + @Test + fun testDistinctViewsSameClassRegisterWithDifferentToken() { + var prev: ViewTransitionToken? = underTest.register(FrameLayout(mContext)) + for (i in 0 until 10) { + val curr = underTest.register(FrameLayout(mContext)) + assertThat(curr).isNotEqualTo(prev) + prev = curr + } + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt index 50762edc1179..88c9e74551fd 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt @@ -21,6 +21,7 @@ import android.content.res.Configuration import android.hardware.biometrics.BiometricAuthenticator import android.hardware.biometrics.BiometricConstants import android.hardware.biometrics.BiometricManager +import android.hardware.biometrics.BiometricPrompt import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton import android.hardware.biometrics.PromptInfo import android.hardware.biometrics.PromptVerticalListContentView @@ -290,7 +291,7 @@ open class AuthContainerViewTest : SysuiTestCase() { verify(callback) .onDismissed( - eq(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED), + eq(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED), eq<ByteArray?>(null), /* credentialAttestation */ eq(authContainer?.requestId ?: 0L), ) @@ -310,7 +311,7 @@ open class AuthContainerViewTest : SysuiTestCase() { ) verify(callback) .onDismissed( - eq(AuthDialogCallback.DISMISSED_USER_CANCELED), + eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL), eq<ByteArray?>(null), /* credentialAttestation */ eq(authContainer?.requestId ?: 0L), ) @@ -325,7 +326,7 @@ open class AuthContainerViewTest : SysuiTestCase() { verify(callback) .onDismissed( - eq(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE), + eq(BiometricPrompt.DISMISSED_REASON_NEGATIVE), eq<ByteArray?>(null), /* credentialAttestation */ eq(authContainer?.requestId ?: 0L), ) @@ -352,7 +353,7 @@ open class AuthContainerViewTest : SysuiTestCase() { verify(callback) .onDismissed( - eq(AuthDialogCallback.DISMISSED_ERROR), + eq(BiometricPrompt.DISMISSED_REASON_ERROR), eq<ByteArray?>(null), /* credentialAttestation */ eq(authContainer?.requestId ?: 0L), ) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java index acc97a9f8642..a1a2aa70d869 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -138,9 +138,9 @@ public class AuthControllerTest extends SysuiTestCase { @Mock private IBiometricContextListener mContextListener; @Mock - private AuthDialog mDialog1; + private AuthContainerView mDialog1; @Mock - private AuthDialog mDialog2; + private AuthContainerView mDialog2; @Mock private CommandQueue mCommandQueue; @Mock @@ -382,7 +382,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testSendsReasonUserCanceled_whenDismissedByUserCancel() throws Exception { showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */); - mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED, + mAuthController.onDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL, null, /* credentialAttestation */ mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( @@ -393,7 +393,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testSendsReasonNegative_whenDismissedByButtonNegative() throws Exception { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); - mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE, + mAuthController.onDismissed(BiometricPrompt.DISMISSED_REASON_NEGATIVE, null, /* credentialAttestation */ mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( @@ -404,7 +404,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testSendsReasonConfirmed_whenDismissedByButtonPositive() throws Exception { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); - mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE, + mAuthController.onDismissed(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED, null, /* credentialAttestation */ mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( @@ -415,7 +415,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testSendsReasonConfirmNotRequired_whenDismissedByAuthenticated() throws Exception { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); - mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED, + mAuthController.onDismissed(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED, null, /* credentialAttestation */ mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( @@ -426,7 +426,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testSendsReasonError_whenDismissedByError() throws Exception { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); - mAuthController.onDismissed(AuthDialogCallback.DISMISSED_ERROR, + mAuthController.onDismissed(BiometricPrompt.DISMISSED_REASON_ERROR, null, /* credentialAttestation */ mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( @@ -437,7 +437,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testSendsReasonServerRequested_whenDismissedByServer() throws Exception { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); - mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER, + mAuthController.onDismissed(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED, null, /* credentialAttestation */ mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( @@ -452,7 +452,7 @@ public class AuthControllerTest extends SysuiTestCase { final byte[] credentialAttestation = generateRandomHAT(); - mAuthController.onDismissed(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED, + mAuthController.onDismissed(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED, credentialAttestation, mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( eq(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED), @@ -462,7 +462,7 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testSendsReasonContentViewMoreOptions_whenButtonPressed() throws Exception { showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */); - mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_CONTENT_VIEW_MORE_OPTIONS, + mAuthController.onDismissed(BiometricPrompt.DISMISSED_REASON_CONTENT_VIEW_MORE_OPTIONS, null, /* credentialAttestation */ mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( @@ -696,7 +696,7 @@ public class AuthControllerTest extends SysuiTestCase { final byte[] credentialAttestation = generateRandomHAT(); - mAuthController.onDismissed(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED, + mAuthController.onDismissed(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED, credentialAttestation, mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( eq(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED), @@ -755,7 +755,7 @@ public class AuthControllerTest extends SysuiTestCase { public void testDoesNotCrash_whenTryAgainPressedAfterDismissal() { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); final long requestID = mAuthController.mCurrentDialog.getRequestId(); - mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED, + mAuthController.onDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL, null, /* credentialAttestation */requestID); mAuthController.onTryAgainPressed(requestID); } @@ -764,7 +764,7 @@ public class AuthControllerTest extends SysuiTestCase { public void testDoesNotCrash_whenDeviceCredentialPressedAfterDismissal() { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); final long requestID = mAuthController.mCurrentDialog.getRequestId(); - mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED, + mAuthController.onDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL, null /* credentialAttestation */, requestID); mAuthController.onDeviceCredentialPressed(requestID); } @@ -818,7 +818,7 @@ public class AuthControllerTest extends SysuiTestCase { // WHEN dialog is shown and then dismissed showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */); - mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED, + mAuthController.onDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL, null /* credentialAttestation */, mAuthController.mCurrentDialog.getRequestId()); @@ -1218,14 +1218,14 @@ public class AuthControllerTest extends SysuiTestCase { } @Override - protected AuthDialog buildDialog(DelayableExecutor bgExecutor, PromptInfo promptInfo, + protected AuthContainerView buildDialog(DelayableExecutor bgExecutor, PromptInfo promptInfo, boolean requireConfirmation, int userId, int[] sensorIds, String opPackageName, boolean skipIntro, long operationId, long requestId, WakefulnessLifecycle wakefulnessLifecycle, UserManager userManager, LockPatternUtils lockPatternUtils, PromptViewModel viewModel) { - AuthDialog dialog; + AuthContainerView dialog; if (mBuildCount == 0) { dialog = mDialog1; } else if (mBuildCount == 1) { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt index 98486a22854a..af6c65ec6d6d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt @@ -538,7 +538,6 @@ object TestShortcuts { simpleShortcutCategory(System, "System apps", "Open settings"), simpleShortcutCategory(System, "System controls", "Lock screen"), simpleShortcutCategory(System, "System controls", "View notifications"), - simpleShortcutCategory(System, "System apps", "Take a note"), simpleShortcutCategory(System, "System controls", "Take screenshot"), simpleShortcutCategory(System, "System controls", "Go back"), simpleShortcutCategory(MultiTasking, "Split screen", "Use full screen"), @@ -570,7 +569,6 @@ object TestShortcuts { simpleInputGestureData( keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL ), - simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES), simpleInputGestureData( keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT ), diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt index 208abf39611d..6c4325adced4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt @@ -35,12 +35,20 @@ package com.android.systemui.keyguard.domain.interactor import android.os.PowerManager import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags +import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.Flags +import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2 import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository +import com.android.systemui.common.data.repository.batteryRepository +import com.android.systemui.common.data.repository.fake +import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository +import com.android.systemui.communal.domain.interactor.communalSceneInteractor +import com.android.systemui.communal.domain.interactor.setCommunalV2Available +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository @@ -54,6 +62,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat +import com.android.systemui.kosmos.collectLastValue +import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testScope import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest @@ -62,6 +72,8 @@ import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor import com.android.systemui.testKosmos +import com.android.systemui.util.settings.fakeSettings +import com.google.common.truth.Truth import junit.framework.Assert.assertEquals import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.advanceTimeBy @@ -416,4 +428,25 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { assertThat(transitionRepository) .startedTransition(from = KeyguardState.AOD, to = KeyguardState.LOCKSCREEN) } + + @Test + @EnableFlags(FLAG_GLANCEABLE_HUB_V2) + fun testTransitionToGlanceableHub_onWakeUpFromAod() = + kosmos.runTest { + val user = setCommunalV2Available(true) + fakeSettings.putIntForUser(Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, 1, user.id) + batteryRepository.fake.setDevicePluggedIn(true) + + val currentScene by collectLastValue(communalSceneInteractor.currentScene) + fakeCommunalSceneRepository.changeScene(CommunalScenes.Blank) + + // Communal is not showing + Truth.assertThat(currentScene).isEqualTo(CommunalScenes.Blank) + + powerInteractor.setAwakeForTest() + testScope.advanceTimeBy(100) // account for debouncing + + Truth.assertThat(currentScene).isEqualTo(CommunalScenes.Communal) + assertThat(transitionRepository).noTransitionsStarted() + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToDreamingTransitionViewModelTest.kt new file mode 100644 index 000000000000..052dfd52887f --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToDreamingTransitionViewModelTest.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2025 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.keyguard.ui.viewmodel + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.kosmos.collectValues +import com.android.systemui.kosmos.runTest +import com.android.systemui.kosmos.testScope +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class DozingToDreamingTransitionViewModelTest : SysuiTestCase() { + val kosmos = testKosmos() + + val underTest by lazy { kosmos.dozingToDreamingTransitionViewModel } + + @Test + fun notificationShadeAlpha() = + kosmos.runTest { + val values by collectValues(underTest.notificationAlpha) + assertThat(values).isEmpty() + + fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.DOZING, + to = KeyguardState.DREAMING, + testScope, + ) + + assertThat(values).isNotEmpty() + values.forEach { assertThat(it).isEqualTo(0) } + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionStateTest.kt index 645efae16b8b..ab217a3f50ef 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionStateTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionStateTest.kt @@ -31,21 +31,18 @@ class MutableSelectionStateTest : SysuiTestCase() { @Test fun selectTile_isCorrectlySelected() { - assertThat(underTest.selection?.tileSpec).isNotEqualTo(TEST_SPEC) + assertThat(underTest.selection).isNotEqualTo(TEST_SPEC) - underTest.select(TEST_SPEC, manual = true) - assertThat(underTest.selection?.tileSpec).isEqualTo(TEST_SPEC) - assertThat(underTest.selection?.manual).isTrue() + underTest.select(TEST_SPEC) + assertThat(underTest.selection).isEqualTo(TEST_SPEC) underTest.unSelect() assertThat(underTest.selection).isNull() val newSpec = TileSpec.create("newSpec") - underTest.select(TEST_SPEC, manual = true) - underTest.select(newSpec, manual = false) - assertThat(underTest.selection?.tileSpec).isNotEqualTo(TEST_SPEC) - assertThat(underTest.selection?.tileSpec).isEqualTo(newSpec) - assertThat(underTest.selection?.manual).isFalse() + underTest.select(TEST_SPEC) + underTest.select(newSpec) + assertThat(underTest.selection).isEqualTo(newSpec) } companion object { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java index fee358a7c15d..83860ecf168b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java @@ -63,6 +63,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.List; +import java.util.Optional; import platform.test.runner.parameterized.ParameterizedAndroidJunit4; import platform.test.runner.parameterized.Parameters; @@ -100,7 +101,7 @@ public class RotationLockTileTest extends SysuiTestCase { @Mock private BatteryController mBatteryController; @Mock - DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController; + Optional<DeviceStateRotationLockSettingController> mDeviceStateRotationLockSettingController; @Mock RotationPolicyWrapper mRotationPolicyWrapper; @Mock diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt index c7b3175a636f..e044d1db92a9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt @@ -35,14 +35,14 @@ import com.android.systemui.statusbar.chips.ui.model.ColorsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer import com.android.systemui.statusbar.core.StatusBarConnectedDisplays -import com.android.systemui.statusbar.core.StatusBarRootModernization import com.android.systemui.statusbar.notification.data.model.activeNotificationModel import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel import com.android.systemui.statusbar.notification.shared.CallType -import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization +import com.android.systemui.statusbar.phone.ongoingcall.DisableChipsModernization +import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel @@ -314,7 +314,7 @@ class CallChipViewModelTest : SysuiTestCase() { } @Test - @DisableFlags(StatusBarChipsModernization.FLAG_NAME) + @DisableChipsModernization fun chip_inCall_nullIntent_nullClickListener() = testScope.runTest { val latest by collectLastValue(underTest.chip) @@ -325,7 +325,7 @@ class CallChipViewModelTest : SysuiTestCase() { } @Test - @DisableFlags(StatusBarChipsModernization.FLAG_NAME) + @DisableChipsModernization fun chip_inCall_positiveStartTime_validIntent_clickListenerLaunchesIntent() = testScope.runTest { val latest by collectLastValue(underTest.chip) @@ -343,7 +343,7 @@ class CallChipViewModelTest : SysuiTestCase() { } @Test - @DisableFlags(StatusBarChipsModernization.FLAG_NAME) + @DisableChipsModernization fun chip_inCall_zeroStartTime_validIntent_clickListenerLaunchesIntent() = testScope.runTest { val latest by collectLastValue(underTest.chip) @@ -362,7 +362,7 @@ class CallChipViewModelTest : SysuiTestCase() { } @Test - @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME) + @EnableChipsModernization fun chip_inCall_nullIntent_noneClickBehavior() = testScope.runTest { val latest by collectLastValue(underTest.chip) @@ -378,7 +378,7 @@ class CallChipViewModelTest : SysuiTestCase() { } @Test - @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME) + @EnableChipsModernization fun chip_inCall_positiveStartTime_validIntent_clickBehaviorLaunchesIntent() = testScope.runTest { val latest by collectLastValue(underTest.chip) @@ -403,7 +403,7 @@ class CallChipViewModelTest : SysuiTestCase() { } @Test - @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME) + @EnableChipsModernization fun chip_inCall_zeroStartTime_validIntent_clickBehaviorLaunchesIntent() = testScope.runTest { val latest by collectLastValue(underTest.chip) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt index 6cfad8540491..005af366a6c0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt @@ -17,8 +17,6 @@ package com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel import android.content.DialogInterface -import android.platform.test.annotations.DisableFlags -import android.platform.test.annotations.EnableFlags import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -44,10 +42,10 @@ import com.android.systemui.statusbar.chips.ui.model.ColorsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.getStopActionFromDialog -import com.android.systemui.statusbar.core.StatusBarRootModernization import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory -import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization +import com.android.systemui.statusbar.phone.ongoingcall.DisableChipsModernization +import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization import com.android.systemui.testKosmos import com.android.systemui.util.time.fakeSystemClock import com.google.common.truth.Truth.assertThat @@ -282,7 +280,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() { } @Test - @DisableFlags(StatusBarChipsModernization.FLAG_NAME) + @DisableChipsModernization fun chip_notProjecting_clickListenerShowsDialog() = testScope.runTest { val latest by collectLastValue(underTest.chip) @@ -299,7 +297,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() { } @Test - @DisableFlags(StatusBarChipsModernization.FLAG_NAME) + @DisableChipsModernization fun chip_projectingEntireScreen_clickListenerShowsDialog() = testScope.runTest { val latest by collectLastValue(underTest.chip) @@ -317,7 +315,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() { } @Test - @DisableFlags(StatusBarChipsModernization.FLAG_NAME) + @DisableChipsModernization fun chip_projectingSingleTask_clickListenerShowsDialog() = testScope.runTest { val latest by collectLastValue(underTest.chip) @@ -339,7 +337,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() { } @Test - @DisableFlags(StatusBarChipsModernization.FLAG_NAME) + @DisableChipsModernization fun chip_clickListenerHasCujLegacy() = testScope.runTest { val latest by collectLastValue(underTest.chip) @@ -359,7 +357,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() { } @Test - @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME) + @EnableChipsModernization fun chip_recordingState_hasClickBehavior() = testScope.runTest { val latest by collectLastValue(underTest.chip) @@ -370,7 +368,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() { } @Test - @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME) + @EnableChipsModernization fun chip_notProjecting_expandActionBehaviorShowsDialog() = testScope.runTest { val latest by collectLastValue(underTest.chip) @@ -386,7 +384,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() { } @Test - @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME) + @EnableChipsModernization fun chip_projectingEntireScreen_expandActionBehaviorShowsDialog() = testScope.runTest { val latest by collectLastValue(underTest.chip) @@ -401,7 +399,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() { } @Test - @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME) + @EnableChipsModernization fun chip_projectingSingleTask_expandActionBehaviorShowsDialog() = testScope.runTest { val latest by collectLastValue(underTest.chip) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt index e708382799c2..d6b10a89726e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt @@ -49,10 +49,10 @@ import com.android.systemui.statusbar.chips.ui.model.ColorsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.getStopActionFromDialog -import com.android.systemui.statusbar.core.StatusBarRootModernization import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory -import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization +import com.android.systemui.statusbar.phone.ongoingcall.DisableChipsModernization +import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization import com.android.systemui.testKosmos import com.android.systemui.util.time.fakeSystemClock import com.google.common.truth.Truth.assertThat @@ -506,7 +506,7 @@ class ShareToAppChipViewModelTest : SysuiTestCase() { @Test @EnableFlags(FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP) - @DisableFlags(StatusBarChipsModernization.FLAG_NAME) + @DisableChipsModernization fun chip_noScreen_clickListenerShowsGenericShareDialog() = testScope.runTest { val latest by collectLastValue(underTest.chip) @@ -527,7 +527,7 @@ class ShareToAppChipViewModelTest : SysuiTestCase() { } @Test - @DisableFlags(StatusBarChipsModernization.FLAG_NAME) + @DisableChipsModernization fun chip_entireScreen_clickListenerShowsScreenShareDialog() = testScope.runTest { val latest by collectLastValue(underTest.chip) @@ -548,7 +548,7 @@ class ShareToAppChipViewModelTest : SysuiTestCase() { } @Test - @DisableFlags(StatusBarChipsModernization.FLAG_NAME) + @DisableChipsModernization fun chip_singleTask_clickListenerShowsScreenShareDialog() = testScope.runTest { val latest by collectLastValue(underTest.chip) @@ -573,7 +573,7 @@ class ShareToAppChipViewModelTest : SysuiTestCase() { } @Test - @DisableFlags(StatusBarChipsModernization.FLAG_NAME) + @DisableChipsModernization fun chip_clickListenerHasCuj() = testScope.runTest { val latest by collectLastValue(underTest.chip) @@ -597,7 +597,7 @@ class ShareToAppChipViewModelTest : SysuiTestCase() { } @Test - @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME) + @EnableChipsModernization fun chip_noScreen_hasClickBehavior() = testScope.runTest { val latest by collectLastValue(underTest.chip) @@ -609,7 +609,7 @@ class ShareToAppChipViewModelTest : SysuiTestCase() { } @Test - @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME) + @EnableChipsModernization fun chip_entireScreen_hasClickBehavior() = testScope.runTest { val latest by collectLastValue(underTest.chip) @@ -621,7 +621,7 @@ class ShareToAppChipViewModelTest : SysuiTestCase() { } @Test - @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME) + @EnableChipsModernization fun chip_singleTask_hasClickBehavior() = testScope.runTest { val latest by collectLastValue(underTest.chip) @@ -637,11 +637,8 @@ class ShareToAppChipViewModelTest : SysuiTestCase() { } @Test - @EnableFlags( - FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP, - StatusBarRootModernization.FLAG_NAME, - StatusBarChipsModernization.FLAG_NAME, - ) + @EnableFlags(FLAG_STATUS_BAR_SHOW_AUDIO_ONLY_PROJECTION_CHIP) + @EnableChipsModernization fun chip_noScreen_clickBehaviorShowsGenericShareDialog() = testScope.runTest { val latest by collectLastValue(underTest.chip) @@ -657,7 +654,7 @@ class ShareToAppChipViewModelTest : SysuiTestCase() { } @Test - @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME) + @EnableChipsModernization fun chip_entireScreen_clickBehaviorShowsScreenShareDialog() = testScope.runTest { val latest by collectLastValue(underTest.chip) @@ -673,7 +670,7 @@ class ShareToAppChipViewModelTest : SysuiTestCase() { } @Test - @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME) + @EnableChipsModernization fun chip_singleTask_clickBehaviorShowsScreenShareDialog() = testScope.runTest { val latest by collectLastValue(underTest.chip) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt index fc3af11c30b3..39b19d3c4191 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.chips.ui.viewmodel -import android.platform.test.annotations.DisableFlags -import android.platform.test.annotations.EnableFlags import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -31,9 +29,9 @@ import com.android.systemui.res.R import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickCallback import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickListener -import com.android.systemui.statusbar.core.StatusBarRootModernization import com.android.systemui.statusbar.phone.SystemUIDialog -import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization +import com.android.systemui.statusbar.phone.ongoingcall.DisableChipsModernization +import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization import kotlin.test.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyBoolean @@ -64,7 +62,7 @@ class OngoingActivityChipViewModelTest : SysuiTestCase() { mock<Expandable>().apply { whenever(dialogTransitionController(any())).thenReturn(mock()) } @Test - @DisableFlags(StatusBarChipsModernization.FLAG_NAME) + @DisableChipsModernization fun createDialogLaunchOnClickListener_showsDialogOnClick() { val cuj = DialogCuj(Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP, tag = "Test") val clickListener = @@ -82,7 +80,7 @@ class OngoingActivityChipViewModelTest : SysuiTestCase() { } @Test - @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME) + @EnableChipsModernization fun createDialogLaunchOnClickCallback_showsDialogOnClick() { val cuj = DialogCuj(Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP, tag = "Test") val clickCallback = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt index 9d17348d95a0..bab349aa7a74 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.phone.ongoingcall.domain.interactor import android.app.PendingIntent -import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -29,12 +28,10 @@ import com.android.systemui.kosmos.collectLastValue import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.statusbar.StatusBarIconView -import com.android.systemui.statusbar.core.StatusBarRootModernization import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository import com.android.systemui.statusbar.gesture.swipeStatusBarAwayGestureHandler -import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel -import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization +import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.addOngoingCallState import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.removeOngoingCallState @@ -52,10 +49,9 @@ import org.mockito.kotlin.verify @SmallTest @RunWith(AndroidJUnit4::class) -@EnableFlags(StatusBarChipsModernization.FLAG_NAME, StatusBarRootModernization.FLAG_NAME) +@EnableChipsModernization class OngoingCallInteractorTest : SysuiTestCase() { private val kosmos = Kosmos().useUnconfinedTestDispatcher() - private val repository = kosmos.activeNotificationListRepository private val underTest = kosmos.ongoingCallInteractor @Before diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt index cd8ca23e0964..51484baf1b7b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt @@ -86,6 +86,7 @@ import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataS import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher import com.android.systemui.statusbar.phone.data.repository.fakeDarkIconRepository +import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization import com.android.systemui.statusbar.pipeline.shared.domain.interactor.setHomeStatusBarIconBlockList import com.android.systemui.statusbar.pipeline.shared.domain.interactor.setHomeStatusBarInteractorShowOperatorName @@ -835,7 +836,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { } @Test - @EnableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME) + @EnableChipsModernization fun isNotificationIconContainerVisible_anyChipShowing_ChipsModernizationOn() = kosmos.runTest { val latest by collectLastValue(underTest.isNotificationIconContainerVisible) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java index 8593f6a08b5a..605e4a47275b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java @@ -37,15 +37,19 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Optional; + @RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper @SmallTest public class RotationLockControllerImplTest extends SysuiTestCase { - private static final String[] DEFAULT_SETTINGS = new String[] {"0:0", "1:2"}; + private static final String[] DEFAULT_SETTINGS = new String[]{"0:0", "1:2"}; - @Mock RotationPolicyWrapper mRotationPolicyWrapper; - @Mock DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController; + @Mock + RotationPolicyWrapper mRotationPolicyWrapper; + @Mock + DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController; private ArgumentCaptor<RotationPolicy.RotationPolicyListener> mRotationPolicyListenerCaptor; @@ -93,7 +97,7 @@ public class RotationLockControllerImplTest extends SysuiTestCase { private void createRotationLockController(String[] deviceStateRotationLockDefaults) { new RotationLockControllerImpl( mRotationPolicyWrapper, - mDeviceStateRotationLockSettingController, + Optional.of(mDeviceStateRotationLockSettingController), deviceStateRotationLockDefaults); } } diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 6fe598435433..3fc46ed6c9d1 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2560,6 +2560,9 @@ <!-- SysUI Tuner: Other section --> <string name="other">Other</string> + <!-- Accessibility description of action to toggle QS tile size on click. It will read as "Double-tap to toggle the tile's size" in screen readers [CHAR LIMIT=NONE] --> + <string name="accessibility_qs_edit_toggle_tile_size_action">toggle the tile\'s size</string> + <!-- Accessibility description of action to remove QS tile on click. It will read as "Double-tap to remove tile" in screen readers [CHAR LIMIT=NONE] --> <string name="accessibility_qs_edit_remove_tile_action">remove tile</string> diff --git a/packages/SystemUI/src/com/android/systemui/ailabs/OWNERS b/packages/SystemUI/src/com/android/systemui/ailabs/OWNERS index 429b4b0fccab..329aa07fdd9e 100644 --- a/packages/SystemUI/src/com/android/systemui/ailabs/OWNERS +++ b/packages/SystemUI/src/com/android/systemui/ailabs/OWNERS @@ -3,6 +3,5 @@ dupin@google.com linyuh@google.com pauldpong@google.com -praveenj@google.com vicliang@google.com yuklimko@google.com diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index b6537118324e..4c8a8f1c13d7 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -33,6 +33,7 @@ import android.graphics.PixelFormat; import android.hardware.biometrics.BiometricAuthenticator.Modality; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricManager.Authenticators; +import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.PromptInfo; import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; @@ -100,7 +101,7 @@ import javax.inject.Provider; */ @Deprecated public class AuthContainerView extends LinearLayout - implements AuthDialog, WakefulnessLifecycle.Observer, CredentialView.Host { + implements WakefulnessLifecycle.Observer, CredentialView.Host { private static final String TAG = "AuthContainerView"; @@ -158,12 +159,11 @@ public class AuthContainerView extends LinearLayout private final Set<Integer> mFailedModalities = new HashSet<Integer>(); private final OnBackInvokedCallback mBackCallback = this::onBackInvoked; - private final @Background DelayableExecutor mBackgroundExecutor; private final MSDLPlayer mMSDLPlayer; // Non-null only if the dialog is in the act of dismissing and has not sent the reason yet. - @Nullable @AuthDialogCallback.DismissedReason private Integer mPendingCallbackReason; + @Nullable @BiometricPrompt.DismissedReason private Integer mPendingCallbackReason; // HAT received from LockSettingsService when credential is verified. @Nullable private byte[] mCredentialAttestation; @@ -188,18 +188,18 @@ public class AuthContainerView extends LinearLayout final class BiometricCallback implements Spaghetti.Callback { @Override public void onAuthenticated() { - animateAway(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED); + animateAway(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED); } @Override public void onUserCanceled() { sendEarlyUserCanceled(); - animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); + animateAway(BiometricPrompt.DISMISSED_REASON_USER_CANCEL); } @Override public void onButtonNegative() { - animateAway(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE); + animateAway(BiometricPrompt.DISMISSED_REASON_NEGATIVE); } @Override @@ -210,12 +210,12 @@ public class AuthContainerView extends LinearLayout @Override public void onContentViewMoreOptionsButtonPressed() { - animateAway(AuthDialogCallback.DISMISSED_BUTTON_CONTENT_VIEW_MORE_OPTIONS); + animateAway(BiometricPrompt.DISMISSED_REASON_CONTENT_VIEW_MORE_OPTIONS); } @Override public void onError() { - animateAway(AuthDialogCallback.DISMISSED_ERROR); + animateAway(BiometricPrompt.DISMISSED_REASON_ERROR); } @Override @@ -234,20 +234,20 @@ public class AuthContainerView extends LinearLayout @Override public void onAuthenticatedAndConfirmed() { - animateAway(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE); + animateAway(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED); } } @Override public void onCredentialMatched(@NonNull byte[] attestation) { mCredentialAttestation = attestation; - animateAway(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED); + animateAway(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED); } @Override public void onCredentialAborted() { sendEarlyUserCanceled(); - animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); + animateAway(BiometricPrompt.DISMISSED_REASON_USER_CANCEL); } @Override @@ -277,7 +277,7 @@ public class AuthContainerView extends LinearLayout com.android.settingslib.R.string.failed_attempts_now_wiping_dialog_dismiss, null /* OnClickListener */) .setOnDismissListener( - dialog -> animateAway(AuthDialogCallback.DISMISSED_ERROR)) + dialog -> animateAway(BiometricPrompt.DISMISSED_REASON_ERROR)) .create(); alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); alertDialog.show(); @@ -349,7 +349,6 @@ public class AuthContainerView extends LinearLayout mPanelView = mLayout.findViewById(R.id.panel); mPanelController = new AuthPanelController(mContext, mPanelView); - mBackgroundExecutor = bgExecutor; mInteractionJankMonitor = jankMonitor; mCredentialViewModelProvider = credentialViewModelProvider; @@ -394,7 +393,7 @@ public class AuthContainerView extends LinearLayout @VisibleForTesting public void onBackInvoked() { sendEarlyUserCanceled(); - animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); + animateAway(BiometricPrompt.DISMISSED_REASON_USER_CANCEL); } void sendEarlyUserCanceled() { @@ -402,7 +401,6 @@ public class AuthContainerView extends LinearLayout BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL, getRequestId()); } - @Override public boolean isAllowDeviceCredentials() { return Utils.isDeviceCredentialAllowed(mConfig.mPromptInfo); } @@ -450,7 +448,6 @@ public class AuthContainerView extends LinearLayout mPanelController.setContainerDimensions(getMeasuredWidth(), getMeasuredHeight()); } - @Override public void onOrientationChanged() { } @@ -538,10 +535,9 @@ public class AuthContainerView extends LinearLayout @Override public void onStartedGoingToSleep() { - animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); + animateAway(BiometricPrompt.DISMISSED_REASON_USER_CANCEL); } - @Override public void show(WindowManager wm) { wm.addView(this, getLayoutParams(mWindowToken, mConfig.mPromptInfo.getTitle())); } @@ -559,7 +555,6 @@ public class AuthContainerView extends LinearLayout } } - @Override public void dismissWithoutCallback(boolean animate) { if (animate) { animateAway(false /* sendReason */, 0 /* reason */); @@ -569,12 +564,10 @@ public class AuthContainerView extends LinearLayout } } - @Override public void dismissFromSystemServer() { animateAway(false /* sendReason */, 0 /* reason */); } - @Override public void onAuthenticationSucceeded(@Modality int modality) { if (mBiometricView != null) { mBiometricView.onAuthenticationSucceeded(modality); @@ -583,7 +576,6 @@ public class AuthContainerView extends LinearLayout } } - @Override public void onAuthenticationFailed(@Modality int modality, String failureReason) { if (mBiometricView != null) { mFailedModalities.add(modality); @@ -593,7 +585,6 @@ public class AuthContainerView extends LinearLayout } } - @Override public void onHelp(@Modality int modality, String help) { if (mBiometricView != null) { mBiometricView.onHelp(modality, help); @@ -602,7 +593,6 @@ public class AuthContainerView extends LinearLayout } } - @Override public void onError(@Modality int modality, String error) { if (mBiometricView != null) { mBiometricView.onError(modality, error); @@ -611,7 +601,6 @@ public class AuthContainerView extends LinearLayout } } - @Override public void onPointerDown() { if (mBiometricView != null) { if (mFailedModalities.contains(TYPE_FACE)) { @@ -624,22 +613,18 @@ public class AuthContainerView extends LinearLayout } } - @Override public String getOpPackageName() { return mConfig.mOpPackageName; } - @Override public String getClassNameIfItIsConfirmDeviceCredentialActivity() { return mConfig.mPromptInfo.getClassNameIfItIsConfirmDeviceCredentialActivity(); } - @Override public long getRequestId() { return mConfig.mRequestId; } - @Override public void animateToCredentialUI(boolean isError) { if (mBiometricView != null) { mBiometricView.startTransitionToCredentialUI(isError); @@ -648,11 +633,11 @@ public class AuthContainerView extends LinearLayout } } - void animateAway(@AuthDialogCallback.DismissedReason int reason) { + void animateAway(@BiometricPrompt.DismissedReason int reason) { animateAway(true /* sendReason */, reason); } - private void animateAway(boolean sendReason, @AuthDialogCallback.DismissedReason int reason) { + private void animateAway(boolean sendReason, @BiometricPrompt.DismissedReason int reason) { if (mContainerState == STATE_ANIMATING_IN) { Log.w(TAG, "startDismiss(): waiting for onDialogAnimatedIn"); mContainerState = STATE_PENDING_DISMISS; @@ -732,7 +717,7 @@ public class AuthContainerView extends LinearLayout private void onDialogAnimatedIn() { if (mContainerState == STATE_PENDING_DISMISS) { Log.d(TAG, "onDialogAnimatedIn(): mPendingDismissDialog=true, dismissing now"); - animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); + animateAway(BiometricPrompt.DISMISSED_REASON_USER_CANCEL); return; } if (mContainerState == STATE_ANIMATING_OUT || mContainerState == STATE_GONE) { @@ -748,7 +733,6 @@ public class AuthContainerView extends LinearLayout } } - @Override public PromptViewModel getViewModel() { return mPromptViewModel; } @@ -776,7 +760,6 @@ public class AuthContainerView extends LinearLayout return lp; } - @Override public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { pw.println(" isAttachedToWindow=" + isAttachedToWindow()); pw.println(" containerState=" + mContainerState); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index eee5f9e34317..68a282018ba4 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -163,7 +163,7 @@ public class AuthController implements // TODO: These should just be saved from onSaveState private SomeArgs mCurrentDialogArgs; @VisibleForTesting - AuthDialog mCurrentDialog; + AuthContainerView mCurrentDialog; @NonNull private final WindowManager mWindowManager; @NonNull private final DisplayManager mDisplayManager; @@ -222,7 +222,7 @@ public class AuthController implements closeDialog(BiometricPrompt.DISMISSED_REASON_USER_CANCEL, reasonString); } - private void closeDialog(@DismissedReason int reason, String reasonString) { + private void closeDialog(@BiometricPrompt.DismissedReason int reason, String reasonString) { if (isShowing()) { Log.i(TAG, "Close BP, reason :" + reasonString); mCurrentDialog.dismissWithoutCallback(true /* animate */); @@ -511,60 +511,14 @@ public class AuthController implements } @Override - public void onDismissed(@DismissedReason int reason, - @Nullable byte[] credentialAttestation, long requestId) { - + public void onDismissed(@BiometricPrompt.DismissedReason int reason, + @Nullable byte[] credentialAttestation, long requestId) { if (mCurrentDialog != null && requestId != mCurrentDialog.getRequestId()) { Log.w(TAG, "requestId doesn't match, skip onDismissed"); return; } - switch (reason) { - case AuthDialogCallback.DISMISSED_USER_CANCELED: - sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_USER_CANCEL, - credentialAttestation); - break; - - case AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE: - sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_NEGATIVE, - credentialAttestation); - break; - - case AuthDialogCallback.DISMISSED_BUTTON_POSITIVE: - sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED, - credentialAttestation); - break; - - case AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED: - sendResultAndCleanUp( - BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED, - credentialAttestation); - break; - - case AuthDialogCallback.DISMISSED_ERROR: - sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_ERROR, - credentialAttestation); - break; - - case AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER: - sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED, - credentialAttestation); - break; - - case AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED: - sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED, - credentialAttestation); - break; - - case AuthDialogCallback.DISMISSED_BUTTON_CONTENT_VIEW_MORE_OPTIONS: - sendResultAndCleanUp( - BiometricPrompt.DISMISSED_REASON_CONTENT_VIEW_MORE_OPTIONS, - credentialAttestation); - break; - default: - Log.e(TAG, "Unhandled reason: " + reason); - break; - } + sendResultAndCleanUp(reason, credentialAttestation); } @Override @@ -699,7 +653,7 @@ public class AuthController implements mUdfpsController.onAodInterrupt(screenX, screenY, major, minor); } - private void sendResultAndCleanUp(@DismissedReason int reason, + private void sendResultAndCleanUp(@BiometricPrompt.DismissedReason int reason, @Nullable byte[] credentialAttestation) { if (mReceiver == null) { Log.e(TAG, "sendResultAndCleanUp: Receiver is null"); @@ -1244,7 +1198,7 @@ public class AuthController implements final long requestId = args.argl2; // Create a new dialog but do not replace the current one yet. - final AuthDialog newDialog = buildDialog( + final AuthContainerView newDialog = buildDialog( mBackgroundExecutor, promptInfo, requireConfirmation, @@ -1327,7 +1281,7 @@ public class AuthController implements return mContext.createDisplayContext(display).getSystemService(WindowManager.class); } - private void onDialogDismissed(@DismissedReason int reason) { + private void onDialogDismissed(@BiometricPrompt.DismissedReason int reason) { if (DEBUG) Log.d(TAG, "onDialogDismissed: " + reason); if (mCurrentDialog == null) { Log.w(TAG, "Dialog already dismissed"); @@ -1361,7 +1315,7 @@ public class AuthController implements } } - protected AuthDialog buildDialog(@Background DelayableExecutor bgExecutor, + protected AuthContainerView buildDialog(@Background DelayableExecutor bgExecutor, PromptInfo promptInfo, boolean requireConfirmation, int userId, int[] sensorIds, String opPackageName, boolean skipIntro, long operationId, long requestId, @NonNull WakefulnessLifecycle wakefulnessLifecycle, @@ -1389,7 +1343,7 @@ public class AuthController implements @Override public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { - final AuthDialog dialog = mCurrentDialog; + final AuthContainerView dialog = mCurrentDialog; pw.println(" mCachedDisplayInfo=" + mCachedDisplayInfo); pw.println(" mScaleFactor=" + mScaleFactor); pw.println(" fingerprintSensorLocationInNaturalOrientation=" diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java deleted file mode 100644 index 861191671ba9..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2019 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.biometrics; - -import android.hardware.biometrics.BiometricAuthenticator.Modality; -import android.view.WindowManager; - -import com.android.systemui.Dumpable; -import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel; - -/** - * Interface for the biometric dialog UI. - * - * TODO(b/287311775): remove along with legacy controller once flag is removed - */ -@Deprecated -public interface AuthDialog extends Dumpable { - - /** - * Parameters used when laying out {@link AuthBiometricView}, its subclasses, and - * {@link AuthPanelController}. - */ - class LayoutParams { - public final int mMediumHeight; - public final int mMediumWidth; - - public LayoutParams(int mediumWidth, int mediumHeight) { - mMediumWidth = mediumWidth; - mMediumHeight = mediumHeight; - } - } - - /** - * Show the dialog. - * @param wm - */ - void show(WindowManager wm); - - /** - * Dismiss the dialog without sending a callback. - */ - void dismissWithoutCallback(boolean animate); - - /** - * Dismiss the dialog. Animate away. - */ - void dismissFromSystemServer(); - - /** - * Biometric authenticated. May be pending user confirmation, or completed. - */ - void onAuthenticationSucceeded(@Modality int modality); - - /** - * Authentication failed (reject, timeout). Dialog stays showing. - * @param modality sensor modality that triggered the error - * @param failureReason message - */ - void onAuthenticationFailed(@Modality int modality, String failureReason); - - /** - * Authentication rejected, or help message received. - * @param modality sensor modality that triggered the help message - * @param help message - */ - void onHelp(@Modality int modality, String help); - - /** - * Authentication failed. Dialog going away. - * @param modality sensor modality that triggered the error - * @param error message - */ - void onError(@Modality int modality, String error); - - /** UDFPS pointer down event. */ - void onPointerDown(); - - /** - * Get the client's package name - */ - String getOpPackageName(); - - /** - * Get the class name of ConfirmDeviceCredentialActivity. Returns null if the direct caller is - * not ConfirmDeviceCredentialActivity. - */ - String getClassNameIfItIsConfirmDeviceCredentialActivity(); - - /** The requestId of the underlying operation within the framework. */ - long getRequestId(); - - /** - * Animate to credential UI. Typically called after biometric is locked out. - */ - void animateToCredentialUI(boolean isError); - - /** - * @return true if device credential is allowed. - */ - boolean isAllowDeviceCredentials(); - - /** - * Called when the device's orientation changed and the dialog may need to do another - * layout. This is most relevant to UDFPS since configuration changes are not sent by - * the framework in equivalent cases (landscape to reverse landscape) but the dialog - * must remain fixed on the physical sensor location. - */ - void onOrientationChanged(); - - PromptViewModel getViewModel(); -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java index 024c6eaa75bb..31c63d3c57e0 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java @@ -16,40 +16,20 @@ package com.android.systemui.biometrics; -import android.annotation.IntDef; import android.annotation.Nullable; +import android.hardware.biometrics.BiometricPrompt; /** * Callback interface for dialog views. These should be implemented by the controller (e.g. * FingerprintDialogImpl) and passed into their views (e.g. FingerprintDialogView). */ public interface AuthDialogCallback { - - int DISMISSED_USER_CANCELED = 1; - int DISMISSED_BUTTON_NEGATIVE = 2; - int DISMISSED_BUTTON_POSITIVE = 3; - int DISMISSED_BIOMETRIC_AUTHENTICATED = 4; - int DISMISSED_ERROR = 5; - int DISMISSED_BY_SYSTEM_SERVER = 6; - int DISMISSED_CREDENTIAL_AUTHENTICATED = 7; - int DISMISSED_BUTTON_CONTENT_VIEW_MORE_OPTIONS = 8; - - @IntDef({DISMISSED_USER_CANCELED, - DISMISSED_BUTTON_NEGATIVE, - DISMISSED_BUTTON_POSITIVE, - DISMISSED_BIOMETRIC_AUTHENTICATED, - DISMISSED_ERROR, - DISMISSED_BY_SYSTEM_SERVER, - DISMISSED_CREDENTIAL_AUTHENTICATED, - DISMISSED_BUTTON_CONTENT_VIEW_MORE_OPTIONS}) - @interface DismissedReason {} - /** * Invoked when the dialog is dismissed - * @param reason + * @param reason - the {@link BiometricPrompt.DismissedReason} for dismissing * @param credentialAttestation the HAT received from LockSettingsService upon verification */ - void onDismissed(@DismissedReason int reason, + void onDismissed(@BiometricPrompt.DismissedReason int reason, @Nullable byte[] credentialAttestation, long requestId); /** diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java index 3c68e3a09f02..a25faa3a7aec 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java @@ -56,6 +56,7 @@ import com.android.systemui.reardisplay.RearDisplayModule; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsImplementation; import com.android.systemui.recents.RecentsModule; +import com.android.systemui.rotationlock.DeviceStateAutoRotateModule; import com.android.systemui.rotationlock.RotationLockModule; import com.android.systemui.rotationlock.RotationLockNewModule; import com.android.systemui.scene.SceneContainerFrameworkModule; @@ -132,6 +133,7 @@ import javax.inject.Named; CollapsedStatusBarFragmentStartableModule.class, ConnectingDisplayViewModel.StartableModule.class, DefaultBlueprintModule.class, + DeviceStateAutoRotateModule.class, EmergencyGestureModule.class, GestureModule.class, HeadsUpModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index fe5a82cb5b8c..f8cf6b007041 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -105,6 +105,7 @@ import com.android.systemui.qs.footer.dagger.FooterActionsModule; import com.android.systemui.recents.Recents; import com.android.systemui.recordissue.RecordIssueModule; import com.android.systemui.retail.RetailModeModule; +import com.android.systemui.rotationlock.DeviceStateAutoRotateModule.BoundsDeviceStateAutoRotateModule; import com.android.systemui.scene.shared.model.SceneContainerConfig; import com.android.systemui.scene.shared.model.SceneDataSource; import com.android.systemui.scene.shared.model.SceneDataSourceDelegator; @@ -145,6 +146,7 @@ import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.ConfigurationControllerModule; import com.android.systemui.statusbar.phone.LetterboxModule; import com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule; +import com.android.systemui.statusbar.policy.DeviceStateRotationLockSettingController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.PolicyModule; import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController; @@ -391,6 +393,11 @@ public abstract class SystemUIModule { @BindsOptionalOf abstract LockscreenContent optionalLockscreenContent(); + @BindsOptionalOf + @BoundsDeviceStateAutoRotateModule + abstract Optional<DeviceStateRotationLockSettingController> + optionalDeviceStateRotationLockSettingController(); + @SysUISingleton @Binds abstract SystemClock bindSystemClock(SystemClockImpl systemClock); @@ -466,6 +473,16 @@ public abstract class SystemUIModule { return new SceneDataSourceDelegator(applicationScope, config); } + @Provides + @SysUISingleton + static Optional<DeviceStateRotationLockSettingController> + provideDeviceStateRotationLockSettingController( + @BoundsDeviceStateAutoRotateModule + Optional<Optional<DeviceStateRotationLockSettingController>> optionalOfOptional + ) { + return optionalOfOptional.orElseGet(Optional::empty); + } + @Binds abstract SceneDataSource bindSceneDataSource(SceneDataSourceDelegator delegator); diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt index 0908e3b5bf85..a779c4c998a7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt @@ -31,7 +31,6 @@ import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION -import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER @@ -67,7 +66,6 @@ class InputGestureMaps @Inject constructor(private val context: Context) { KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER to System, KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL to System, KEY_GESTURE_TYPE_LOCK_SCREEN to System, - KEY_GESTURE_TYPE_OPEN_NOTES to System, KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS to System, KEY_GESTURE_TYPE_LAUNCH_ASSISTANT to System, KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT to System, @@ -113,7 +111,6 @@ class InputGestureMaps @Inject constructor(private val context: Context) { R.string.shortcut_helper_category_system_controls, KEY_GESTURE_TYPE_LOCK_SCREEN to R.string.shortcut_helper_category_system_controls, KEY_GESTURE_TYPE_ALL_APPS to R.string.shortcut_helper_category_system_controls, - KEY_GESTURE_TYPE_OPEN_NOTES to R.string.shortcut_helper_category_system_apps, KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS to R.string.shortcut_helper_category_system_apps, KEY_GESTURE_TYPE_LAUNCH_ASSISTANT to R.string.shortcut_helper_category_system_apps, @@ -173,7 +170,6 @@ class InputGestureMaps @Inject constructor(private val context: Context) { R.string.group_system_access_notification_shade, KEY_GESTURE_TYPE_LOCK_SCREEN to R.string.group_system_lock_screen, KEY_GESTURE_TYPE_ALL_APPS to R.string.group_system_access_all_apps_search, - KEY_GESTURE_TYPE_OPEN_NOTES to R.string.group_system_quick_memo, KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS to R.string.group_system_access_system_settings, KEY_GESTURE_TYPE_LAUNCH_ASSISTANT to R.string.group_system_access_google_assistant, KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT to diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt index 8bed8537b6c5..a7375f7e7efc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt @@ -178,11 +178,6 @@ constructor(@Main private val resources: Resources, private val inputManager: In private fun systemAppsShortcuts() = listOf( - // Pull up Notes app for quick memo: - // - Meta + Ctrl + N - shortcutInfo(resources.getString(R.string.group_system_quick_memo)) { - command(META_META_ON or META_CTRL_ON, KEYCODE_N) - }, // Access system settings: // - Meta + I shortcutInfo(resources.getString(R.string.group_system_access_system_settings)) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt index 4ad04bef6836..ef06a85bd0d9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt @@ -20,6 +20,10 @@ import android.animation.ValueAnimator import android.util.Log import com.android.app.animation.Interpolators import com.android.app.tracing.coroutines.launchTraced as launch +import com.android.systemui.communal.domain.interactor.CommunalInteractor +import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor +import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main @@ -54,6 +58,9 @@ constructor( keyguardOcclusionInteractor: KeyguardOcclusionInteractor, val deviceEntryRepository: DeviceEntryRepository, private val wakeToGoneInteractor: KeyguardWakeDirectlyToGoneInteractor, + private val communalSettingsInteractor: CommunalSettingsInteractor, + private val communalSceneInteractor: CommunalSceneInteractor, + private val communalInteractor: CommunalInteractor, ) : TransitionInteractor( fromState = KeyguardState.AOD, @@ -103,6 +110,7 @@ constructor( val isKeyguardOccludedLegacy = keyguardInteractor.isKeyguardOccluded.value val biometricUnlockMode = keyguardInteractor.biometricUnlockState.value.mode val primaryBouncerShowing = keyguardInteractor.primaryBouncerShowing.value + val shouldShowCommunal = communalInteractor.shouldShowCommunal.value if (!maybeHandleInsecurePowerGesture()) { val shouldTransitionToLockscreen = @@ -129,6 +137,9 @@ constructor( (!KeyguardWmStateRefactor.isEnabled && canDismissLockscreen()) || (KeyguardWmStateRefactor.isEnabled && canWakeDirectlyToGone) + val shouldTransitionToCommunal = + communalSettingsInteractor.isV2FlagEnabled() && shouldShowCommunal + if (shouldTransitionToGone) { // TODO(b/360368320): Adapt for scene framework if (SceneContainerFlag.isEnabled) return@collect @@ -137,6 +148,11 @@ constructor( modeOnCanceled = TransitionModeOnCanceled.REVERSE, ownerReason = "canWakeDirectlyToGone = true", ) + } else if (shouldTransitionToCommunal) { + communalSceneInteractor.changeScene( + CommunalScenes.Communal, + "listen for aod to communal", + ) } else if (shouldTransitionToLockscreen) { val modeOnCanceled = if (startedStep.from == KeyguardState.LOCKSCREEN) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt index 6f5f662d6fa3..0700ec639153 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt @@ -159,6 +159,7 @@ constructor( val isKeyguardOccludedLegacy = keyguardInteractor.isKeyguardOccluded.value val primaryBouncerShowing = keyguardInteractor.primaryBouncerShowing.value val isKeyguardGoingAway = keyguardInteractor.isKeyguardGoingAway.value + val canStartDreaming = dreamManager.canStartDreaming(false) if (!deviceEntryInteractor.isLockscreenEnabled()) { if (!SceneContainerFlag.isEnabled) { @@ -191,6 +192,13 @@ constructor( if (!SceneContainerFlag.isEnabled) { transitionToGlanceableHub() } + } else if (canStartDreaming) { + // If we're waking up to dream, transition directly to dreaming without + // showing the lockscreen. + startTransitionTo( + KeyguardState.DREAMING, + ownerReason = "moving from doze to dream", + ) } else { startTransitionTo(KeyguardState.LOCKSCREEN) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt index 2eeba0f10e0c..3ad862b761fc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt @@ -77,7 +77,7 @@ constructor( if (!communalSettingsInteractor.isCommunalFlagEnabled()) { return } - listenForHubToDozing() + listenForHubToAodOrDozing() listenForHubToPrimaryBouncer() listenForHubToAlternateBouncer() listenForHubToOccluded() @@ -123,15 +123,15 @@ constructor( } } - private fun listenForHubToDozing() { + private fun listenForHubToAodOrDozing() { scope.launch { powerInteractor.isAsleep .filterRelevantKeyguardStateAnd { isAsleep -> isAsleep } .collect { - communalSceneInteractor.snapToScene( + communalSceneInteractor.changeScene( newScene = CommunalScenes.Blank, - loggingReason = "hub to dozing", - keyguardState = KeyguardState.DOZING, + loggingReason = "hub to sleep", + keyguardState = keyguardInteractor.asleepKeyguardState.value, ) } } @@ -254,5 +254,6 @@ constructor( val TO_LOCKSCREEN_DURATION = 1.seconds val TO_BOUNCER_DURATION = 400.milliseconds val TO_OCCLUDED_DURATION = 450.milliseconds + val TO_AOD_DURATION = 500.milliseconds } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt index dba2578f79da..c4a7e1ed95e1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt @@ -21,6 +21,7 @@ import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransiti import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToLockscreenTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToOccludedTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToPrimaryBouncerTransitionViewModel +import com.android.systemui.keyguard.ui.viewmodel.AodToGlanceableHubTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.AodToGoneTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.AodToLockscreenTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.AodToOccludedTransitionViewModel @@ -33,6 +34,7 @@ import com.android.systemui.keyguard.ui.viewmodel.DozingToPrimaryBouncerTransiti import com.android.systemui.keyguard.ui.viewmodel.DreamingToAodTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.DreamingToGlanceableHubTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel +import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToAodTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToDreamingTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToOccludedTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.GoneToAodTransitionViewModel @@ -118,6 +120,12 @@ abstract class DeviceEntryIconTransitionModule { @Binds @IntoSet + abstract fun aodToGlanceableHub( + impl: AodToGlanceableHubTransitionViewModel + ): DeviceEntryIconTransition + + @Binds + @IntoSet abstract fun dozingToGone(impl: DozingToGoneTransitionViewModel): DeviceEntryIconTransition @Binds @@ -258,6 +266,12 @@ abstract class DeviceEntryIconTransitionModule { @Binds @IntoSet + abstract fun glanceableHubToAod( + impl: GlanceableHubToAodTransitionViewModel + ): DeviceEntryIconTransition + + @Binds + @IntoSet abstract fun occludedToGlanceableHub( impl: OccludedToGlanceableHubTransitionViewModel ): DeviceEntryIconTransition diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGlanceableHubTransitionViewModel.kt new file mode 100644 index 000000000000..45f8f10595e4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGlanceableHubTransitionViewModel.kt @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2025 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.keyguard.ui.viewmodel + +import android.util.MathUtils +import com.android.systemui.Flags.lightRevealMigration +import com.android.systemui.communal.ui.compose.TransitionDuration +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.dagger.GlanceableHubBlurComponent +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.AOD +import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB +import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow +import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import com.android.systemui.keyguard.ui.transitions.GlanceableHubTransition +import com.android.systemui.scene.shared.model.Scenes +import javax.inject.Inject +import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.flow.Flow + +@SysUISingleton +class AodToGlanceableHubTransitionViewModel +@Inject +constructor( + animationFlow: KeyguardTransitionAnimationFlow, + blurFactory: GlanceableHubBlurComponent.Factory, +) : DeviceEntryIconTransition, GlanceableHubTransition { + private val transitionAnimation = + animationFlow + .setup( + duration = TransitionDuration.TO_GLANCEABLE_HUB_DURATION_MS.milliseconds, + edge = Edge.create(AOD, Scenes.Communal), + ) + .setupWithoutSceneContainer(edge = Edge.create(AOD, GLANCEABLE_HUB)) + + override val deviceEntryParentViewAlpha: Flow<Float> = + transitionAnimation.immediatelyTransitionTo(1f) + + /** Fade out the lockscreen during a transition to GLANCEABLE_HUB. */ + fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> { + var currentAlpha = 0f + return transitionAnimation.sharedFlow( + duration = 250.milliseconds, + startTime = + if (lightRevealMigration()) { + 100.milliseconds // Wait for the light reveal to "hit" the LS elements. + } else { + 0.milliseconds + }, + onStart = { + currentAlpha = + if (lightRevealMigration()) { + viewState.alpha() + } else { + 0f + } + }, + onStep = { MathUtils.lerp(currentAlpha, 0f, it) }, + ) + } + + override val windowBlurRadius: Flow<Float> = + blurFactory.create(transitionAnimation).getBlurProvider().enterBlurRadius +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt index 75bba489f893..0ccb24a9858a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt @@ -60,6 +60,7 @@ constructor( primaryBouncerToDozingTransitionViewModel: PrimaryBouncerToDozingTransitionViewModel, primaryBouncerToLockscreenTransitionViewModel: PrimaryBouncerToLockscreenTransitionViewModel, lockscreenToDozingTransitionViewModel: LockscreenToDozingTransitionViewModel, + glanceableHubToAodTransitionViewModel: GlanceableHubToAodTransitionViewModel, ) { val color: Flow<Int> = deviceEntryIconViewModel.useBackgroundProtection.flatMapLatest { useBackground -> @@ -106,6 +107,7 @@ constructor( primaryBouncerToLockscreenTransitionViewModel .deviceEntryBackgroundViewAlpha, lockscreenToDozingTransitionViewModel.deviceEntryBackgroundViewAlpha, + glanceableHubToAodTransitionViewModel.deviceEntryBackgroundViewAlpha, ) .merge() .onStart { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToDreamingTransitionViewModel.kt index e6a85c6860c5..9018c58a7e36 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToDreamingTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToDreamingTransitionViewModel.kt @@ -39,4 +39,6 @@ constructor(animationFlow: KeyguardTransitionAnimationFlow) { ) val lockscreenAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(0f) + // Notifications should not be shown while transitioning to dream. + val notificationAlpha = transitionAnimation.immediatelyTransitionTo(0f) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToAodTransitionViewModel.kt new file mode 100644 index 000000000000..6a45845a02c6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToAodTransitionViewModel.kt @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2025 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.keyguard.ui.viewmodel + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor +import com.android.systemui.keyguard.domain.interactor.FromGlanceableHubTransitionInteractor +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.KeyguardState.AOD +import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow +import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import com.android.systemui.scene.shared.model.Scenes +import javax.inject.Inject +import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest + +@OptIn(ExperimentalCoroutinesApi::class) +@SysUISingleton +class GlanceableHubToAodTransitionViewModel +@Inject +constructor( + deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, + animationFlow: KeyguardTransitionAnimationFlow, +) : DeviceEntryIconTransition { + + private val transitionAnimation = + animationFlow + .setup( + duration = FromGlanceableHubTransitionInteractor.TO_AOD_DURATION, + edge = Edge.create(from = Scenes.Communal, to = AOD), + ) + .setupWithoutSceneContainer(edge = Edge.create(KeyguardState.GLANCEABLE_HUB, AOD)) + + val deviceEntryBackgroundViewAlpha: Flow<Float> = + transitionAnimation.immediatelyTransitionTo(0f) + + /** Lockscreen views alpha */ + val lockscreenAlpha: Flow<Float> = + transitionAnimation.sharedFlow( + startTime = 233.milliseconds, + duration = 250.milliseconds, + onStep = { it }, + ) + + override val deviceEntryParentViewAlpha: Flow<Float> = + deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { udfpsEnrolledAndEnabled + -> + if (udfpsEnrolledAndEnabled) { + transitionAnimation.immediatelyTransitionTo(1f) + } else { + emptyFlow() + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt index 8e21745e1a31..def87a8e2717 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt @@ -100,6 +100,7 @@ constructor( private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel, private val aodToOccludedTransitionViewModel: AodToOccludedTransitionViewModel, private val aodToPrimaryBouncerTransitionViewModel: AodToPrimaryBouncerTransitionViewModel, + private val aodToGlanceableHubTransitionViewModel: AodToGlanceableHubTransitionViewModel, private val dozingToDreamingTransitionViewModel: DozingToDreamingTransitionViewModel, private val dozingToGoneTransitionViewModel: DozingToGoneTransitionViewModel, private val dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel, @@ -111,6 +112,7 @@ constructor( private val dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel, private val glanceableHubToLockscreenTransitionViewModel: GlanceableHubToLockscreenTransitionViewModel, + private val glanceableHubToAodTransitionViewModel: GlanceableHubToAodTransitionViewModel, private val goneToAodTransitionViewModel: GoneToAodTransitionViewModel, private val goneToDozingTransitionViewModel: GoneToDozingTransitionViewModel, private val goneToDreamingTransitionViewModel: GoneToDreamingTransitionViewModel, @@ -258,6 +260,7 @@ constructor( aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState), aodToOccludedTransitionViewModel.lockscreenAlpha(viewState), aodToPrimaryBouncerTransitionViewModel.lockscreenAlpha, + aodToGlanceableHubTransitionViewModel.lockscreenAlpha(viewState), dozingToDreamingTransitionViewModel.lockscreenAlpha, dozingToGoneTransitionViewModel.lockscreenAlpha(viewState), dozingToLockscreenTransitionViewModel.lockscreenAlpha, @@ -267,6 +270,7 @@ constructor( dreamingToGoneTransitionViewModel.lockscreenAlpha, dreamingToLockscreenTransitionViewModel.lockscreenAlpha, glanceableHubToLockscreenTransitionViewModel.keyguardAlpha, + glanceableHubToAodTransitionViewModel.lockscreenAlpha, goneToAodTransitionViewModel.enterFromTopAnimationAlpha, goneToDozingTransitionViewModel.lockscreenAlpha, goneToDreamingTransitionViewModel.lockscreenAlpha, diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt index 302242ca11dd..eb6f97942ec0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt @@ -19,8 +19,6 @@ package com.android.systemui.qs.panels.ui.compose import androidx.compose.runtime.Stable import com.android.compose.animation.Bounceable import com.android.systemui.qs.panels.shared.model.SizedTile -import com.android.systemui.qs.panels.ui.model.GridCell -import com.android.systemui.qs.panels.ui.model.TileGridCell import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel @@ -32,18 +30,6 @@ data class BounceableInfo( val bounceEnd: Boolean, ) -fun List<Pair<GridCell, BounceableTileViewModel>>.bounceableInfo( - index: Int, - columns: Int, -): BounceableInfo { - val cell = this[index].first as TileGridCell - // Only look for neighbor bounceables if they are on the same row - val onLastColumn = cell.onLastColumn(cell.column, columns) - val previousTile = getOrNull(index - 1)?.takeIf { cell.column != 0 } - val nextTile = getOrNull(index + 1)?.takeIf { !onLastColumn } - return BounceableInfo(this[index].second, previousTile?.second, nextTile?.second, !onLastColumn) -} - fun List<BounceableTileViewModel>.bounceableInfo( sizedTile: SizedTile<TileViewModel>, index: Int, diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt index ebfe101948c2..ddadb8879f07 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt @@ -22,7 +22,6 @@ import androidx.compose.animation.AnimatedContent import androidx.compose.animation.animateContentSize import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.animateDpAsState -import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut @@ -34,7 +33,6 @@ import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.clipScrollableContainer import androidx.compose.foundation.gestures.Orientation -import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Arrangement.spacedBy import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope @@ -47,28 +45,27 @@ import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredHeightIn import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyGridScope import androidx.compose.foundation.lazy.grid.rememberLazyGridState import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.systemGestureExclusion import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Clear -import androidx.compose.material.icons.filled.Remove import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.LocalContentColor -import androidx.compose.material3.LocalMinimumInteractiveComponentSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text @@ -91,12 +88,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.Rect import androidx.compose.ui.geometry.isSpecified import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.MeasureScope import androidx.compose.ui.layout.layout import androidx.compose.ui.layout.onGloballyPositioned @@ -113,24 +107,17 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.compose.ui.unit.toSize import androidx.compose.ui.util.fastMap -import androidx.compose.ui.zIndex -import com.android.app.tracing.coroutines.launchTraced as launch -import com.android.compose.animation.bounceable import com.android.compose.modifiers.height import com.android.systemui.common.ui.compose.load import com.android.systemui.qs.panels.shared.model.SizedTile import com.android.systemui.qs.panels.shared.model.SizedTileImpl -import com.android.systemui.qs.panels.ui.compose.BounceableInfo import com.android.systemui.qs.panels.ui.compose.DragAndDropState import com.android.systemui.qs.panels.ui.compose.DragType import com.android.systemui.qs.panels.ui.compose.EditTileListState -import com.android.systemui.qs.panels.ui.compose.bounceableInfo import com.android.systemui.qs.panels.ui.compose.dragAndDropRemoveZone import com.android.systemui.qs.panels.ui.compose.dragAndDropTileList import com.android.systemui.qs.panels.ui.compose.dragAndDropTileSource @@ -142,13 +129,14 @@ import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaul import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.AUTO_SCROLL_SPEED import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.AvailableTilesGridMinHeight import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.CurrentTilesGridPadding -import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.TileBadgeSize +import com.android.systemui.qs.panels.ui.compose.selection.InteractiveTileContainer import com.android.systemui.qs.panels.ui.compose.selection.MutableSelectionState -import com.android.systemui.qs.panels.ui.compose.selection.ResizableTileContainer import com.android.systemui.qs.panels.ui.compose.selection.ResizingState import com.android.systemui.qs.panels.ui.compose.selection.ResizingState.ResizeOperation import com.android.systemui.qs.panels.ui.compose.selection.ResizingState.ResizeOperation.FinalResizeOperation import com.android.systemui.qs.panels.ui.compose.selection.ResizingState.ResizeOperation.TemporaryResizeOperation +import com.android.systemui.qs.panels.ui.compose.selection.StaticTileBadge +import com.android.systemui.qs.panels.ui.compose.selection.TileState import com.android.systemui.qs.panels.ui.compose.selection.rememberResizingState import com.android.systemui.qs.panels.ui.compose.selection.rememberSelectionState import com.android.systemui.qs.panels.ui.compose.selection.selectableTile @@ -252,13 +240,15 @@ fun DefaultEditTileGrid( AnimatedContent( targetState = listState.dragInProgress || selectionState.selected, label = "QSEditHeader", + contentAlignment = Alignment.Center, + modifier = Modifier.fillMaxWidth().heightIn(min = 80.dp), ) { showRemoveTarget -> - EditGridHeader(Modifier.padding(bottom = 26.dp)) { + EditGridHeader { if (showRemoveTarget) { RemoveTileTarget { selectionState.selection?.let { selectionState.unSelect() - onRemoveTile(it.tileSpec) + onRemoveTile(it) } } } else { @@ -380,7 +370,7 @@ private fun RemoveTileTarget(onClick: () -> Unit) { verticalAlignment = Alignment.CenterVertically, horizontalArrangement = tileHorizontalArrangement(), modifier = - Modifier.fillMaxHeight() + Modifier.wrapContentSize() .clickable(onClick = onClick) .border(1.dp, LocalContentColor.current, shape = CircleShape) .padding(10.dp), @@ -430,22 +420,15 @@ private fun CurrentTilesGrid( ) .dragAndDropTileList(gridState, { gridContentOffset }, listState) { spec -> onSetTiles(currentListState.tileSpecs()) - selectionState.select(spec, manual = false) + selectionState.select(spec) } .onGloballyPositioned { coordinates -> gridContentOffset = coordinates.positionInRoot() } .testTag(CURRENT_TILES_GRID_TEST_TAG), ) { - EditTiles( - cells, - columns, - listState, - selectionState, - coroutineScope, - largeTilesSpan, - onRemoveTile, - ) { resizingOperation -> + EditTiles(cells, listState, selectionState, coroutineScope, largeTilesSpan, onRemoveTile) { + resizingOperation -> when (resizingOperation) { is TemporaryResizeOperation -> { currentListState.resizeTile(resizingOperation.spec, resizingOperation.toIcon) @@ -453,10 +436,6 @@ private fun CurrentTilesGrid( is FinalResizeOperation -> { // Commit the new size of the tile onResize(resizingOperation.spec, resizingOperation.toIcon) - - // Mark the selection as automatic in case the tile ends up moving to a - // different row with its new size. - selectionState.select(resizingOperation.spec, manual = false) } } } @@ -536,7 +515,6 @@ private fun GridCell.key(index: Int): Any { * Adds a list of [GridCell] to the lazy grid * * @param cells the pairs of [GridCell] to [BounceableTileViewModel] - * @param columns the number of columns of this tile grid * @param dragAndDropState the [DragAndDropState] for this grid * @param selectionState the [MutableSelectionState] for this grid * @param coroutineScope the [CoroutineScope] to be used for the tiles @@ -545,7 +523,6 @@ private fun GridCell.key(index: Int): Any { */ fun LazyGridScope.EditTiles( cells: List<Pair<GridCell, BounceableTileViewModel>>, - columns: Int, dragAndDropState: DragAndDropState, selectionState: MutableSelectionState, coroutineScope: CoroutineScope, @@ -581,7 +558,6 @@ fun LazyGridScope.EditTiles( onResize = onResize, onRemoveTile = onRemoveTile, coroutineScope = coroutineScope, - bounceableInfo = cells.bounceableInfo(index, columns), largeTilesSpan = largeTilesSpan, modifier = Modifier.animateItem(), ) @@ -601,83 +577,84 @@ private fun TileGridCell( onRemoveTile: (TileSpec) -> Unit, coroutineScope: CoroutineScope, largeTilesSpan: Int, - bounceableInfo: BounceableInfo, modifier: Modifier = Modifier, ) { val stateDescription = stringResource(id = R.string.accessibility_qs_edit_position, index + 1) - var selected by remember { mutableStateOf(false) } - val showRemovalBadge = - !selected && cell.tile.availableEditActions.contains(AvailableEditActions.REMOVE) - val selectionAlpha by - animateFloatAsState( - targetValue = if (selected) 1f else 0f, - label = "QSEditTileSelectionAlpha", - ) - val selectionColor = MaterialTheme.colorScheme.primary - val colors = EditModeTileDefaults.editTileColors() - val currentBounceableInfo by rememberUpdatedState(bounceableInfo) - - LaunchedEffect(selectionState.selection?.tileSpec) { - selectionState.selection?.let { - // A delay is introduced on automatic selections such as dragged tiles or reflow caused - // by resizing. This avoids clipping issues on the border and resizing handle, as well - // as letting the selection animation play correctly. - if (!it.manual) { - delay(250) + val canShowRemovalBadge = cell.tile.availableEditActions.contains(AvailableEditActions.REMOVE) + var tileState by remember { mutableStateOf(TileState.None) } + + LaunchedEffect(selectionState.selection, canShowRemovalBadge) { + tileState = + when { + selectionState.selection == cell.tile.tileSpec -> { + if (tileState == TileState.None && canShowRemovalBadge) { + // The tile decoration is None if a tile is newly composed OR the removal + // badge can't be shown. + // For newly composed and selected tiles, such as dragged tiles or moved + // tiles from resizing, introduce a short delay. This avoids clipping issues + // on the border and resizing handle, as well as letting the selection + // animation play correctly. + delay(250) + } + TileState.Selected + } + canShowRemovalBadge -> TileState.Removable + else -> TileState.None } - } - selected = selectionState.selection?.tileSpec == cell.tile.tileSpec } - val state = rememberResizingState(cell.tile.tileSpec, cell.isIcon) - + val resizingState = rememberResizingState(cell.tile.tileSpec, cell.isIcon) val progress: () -> Float = { - if (selected) { - // If selected, return the manual progress from the drag - state.progress() + if (tileState == TileState.Selected) { + resizingState.progress() } else { - // Else, return the target progress for the tile format if (cell.isIcon) 0f else 1f } } - if (!selected) { + if (tileState != TileState.Selected) { // Update the draggable anchor state when the tile's size is not manually toggled - LaunchedEffect(cell.isIcon) { state.updateCurrentValue(cell.isIcon) } + LaunchedEffect(cell.isIcon) { resizingState.updateCurrentValue(cell.isIcon) } } else { // If the tile is selected, listen to new target values from the draggable anchor to toggle // the tile's size - LaunchedEffect(state.temporaryResizeOperation) { onResize(state.temporaryResizeOperation) } - LaunchedEffect(state.finalResizeOperation) { onResize(state.finalResizeOperation) } + LaunchedEffect(resizingState.temporaryResizeOperation) { + onResize(resizingState.temporaryResizeOperation) + } + LaunchedEffect(resizingState.finalResizeOperation) { + onResize(resizingState.finalResizeOperation) + } } val totalPadding = with(LocalDensity.current) { (largeTilesSpan - 1) * TileArrangementPadding.roundToPx() } - - ResizableTileContainer( - selected = selected, - state = state, - selectionAlpha = { selectionAlpha }, - selectionColor = selectionColor, + val colors = EditModeTileDefaults.editTileColors() + val toggleSizeLabel = stringResource(R.string.accessibility_qs_edit_toggle_tile_size_action) + val clickLabel = + when (tileState) { + TileState.None -> null + TileState.Removable -> + stringResource(id = R.string.accessibility_qs_edit_remove_tile_action) + TileState.Selected -> toggleSizeLabel + } + InteractiveTileContainer( + tileState = tileState, + resizingState = resizingState, modifier = - modifier - .height(TileHeight) - .fillMaxWidth() - .onSizeChanged { - // Grab the size before the bounceable to get the idle width - val min = - if (cell.isIcon) it.width else (it.width - totalPadding) / largeTilesSpan - val max = - if (cell.isIcon) (it.width * largeTilesSpan) + totalPadding else it.width - state.updateAnchors(min.toFloat(), max.toFloat()) - } - .bounceable( - bounceable = currentBounceableInfo.bounceable, - previousBounceable = currentBounceableInfo.previousTile, - nextBounceable = currentBounceableInfo.nextTile, - orientation = Orientation.Horizontal, - bounceEnd = currentBounceableInfo.bounceEnd, - ), + modifier.height(TileHeight).fillMaxWidth().onSizeChanged { + // Calculate the min/max width from the idle size + val min = if (cell.isIcon) it.width else (it.width - totalPadding) / largeTilesSpan + val max = if (cell.isIcon) (it.width * largeTilesSpan) + totalPadding else it.width + resizingState.updateAnchors(min.toFloat(), max.toFloat()) + }, + onClick = { + if (tileState == TileState.Removable) { + onRemoveTile(cell.tile.tileSpec) + } else if (tileState == TileState.Selected) { + coroutineScope.launch { resizingState.toggleCurrentValue() } + } + }, + onClickLabel = clickLabel, ) { Box( modifier @@ -688,15 +665,13 @@ private fun TileGridCell( customActions = listOf( // TODO(b/367748260): Add final accessibility actions - CustomAccessibilityAction("Toggle size") { + CustomAccessibilityAction(toggleSizeLabel) { onResize(FinalResizeOperation(cell.tile.tileSpec, !cell.isIcon)) true } ) } - .selectableTile(cell.tile.tileSpec, selectionState) { - coroutineScope.launch { currentBounceableInfo.bounceable.animateBounce() } - } + .selectableTile(cell.tile.tileSpec, selectionState) .dragAndDropTileSource( SizedTileImpl(cell.tile, cell.width), dragAndDropState, @@ -705,16 +680,7 @@ private fun TileGridCell( ) .tileBackground(colors.background) ) { - EditTile(tile = cell.tile, state = state, progress = progress) - } - - if (showRemovalBadge) { - TileBadge( - icon = Icons.Default.Remove, - contentDescription = stringResource(R.string.qs_customize_remove), - ) { - onRemoveTile(cell.tile.tileSpec) - } + EditTile(tile = cell.tile, state = resizingState, progress = progress) } } } @@ -733,7 +699,7 @@ private fun AvailableTileGridCell( val colors = EditModeTileDefaults.editTileColors() val onClick = { onAddTile(cell.tile.tileSpec) - selectionState.select(cell.tile.tileSpec, manual = false) + selectionState.select(cell.tile.tileSpec) } // Displays the tile as an icon tile with the label underneath @@ -742,10 +708,9 @@ private fun AvailableTileGridCell( verticalArrangement = spacedBy(CommonTileDefaults.TilePadding, Alignment.Top), modifier = modifier, ) { - Box { + Box(Modifier.fillMaxWidth().height(TileHeight)) { Box( - Modifier.fillMaxWidth() - .height(TileHeight) + Modifier.fillMaxSize() .clickable(onClick = onClick, onClickLabel = onClickActionName) .semantics(mergeDescendants = true) { this.stateDescription = stateDescription } .dragAndDropTileSource( @@ -756,7 +721,6 @@ private fun AvailableTileGridCell( selectionState.unSelect() } .tileBackground(colors.background) - .tilePadding() ) { // Icon SmallTileContent( @@ -767,7 +731,7 @@ private fun AvailableTileGridCell( ) } - TileBadge( + StaticTileBadge( icon = Icons.Default.Add, contentDescription = onClickActionName, onClick = onClick, @@ -787,39 +751,6 @@ private fun AvailableTileGridCell( } @Composable -private fun TileBadge(icon: ImageVector, contentDescription: String?, onClick: () -> Unit) { - // Use a higher zIndex than the tile to draw over it, and manually create the touch target as - // we're drawing over neighbor tiles as well. - val minTouchTargetSize = LocalMinimumInteractiveComponentSize.current - - Box( - Modifier.zIndex(2f) - .layout { measurable, constraints -> - val size = minTouchTargetSize.roundToPx() - val placeable = measurable.measure(Constraints(size)) - layout(placeable.width, placeable.height) { - val iconRadius = TileBadgeSize.roundToPx() / 2 - val x = constraints.maxWidth - placeable.width / 2 - iconRadius - val y = 0 - placeable.height / 2 + iconRadius - placeable.place(x, y) - } - } - .systemGestureExclusion { Rect(Offset.Zero, it.size.toSize()) } - .pointerInput(Unit) { detectTapGestures { onClick() } } - ) { - val secondaryColor = MaterialTheme.colorScheme.secondary - Icon( - icon, - contentDescription = contentDescription, - modifier = - Modifier.size(TileBadgeSize).align(Alignment.Center).drawBehind { - drawCircle(secondaryColor) - }, - ) - } -} - -@Composable private fun SpacerGridCell(modifier: Modifier = Modifier) { // By default, spacers are invisible and exist purely to catch drag movements Box(modifier.height(TileHeight).fillMaxWidth()) @@ -902,9 +833,8 @@ private object EditModeTileDefaults { const val PLACEHOLDER_ALPHA = .3f const val AUTO_SCROLL_DISTANCE = 100 const val AUTO_SCROLL_SPEED = 2 // 2ms per pixel - val CurrentTilesGridPadding = 8.dp + val CurrentTilesGridPadding = 10.dp val AvailableTilesGridMinHeight = 200.dp - val TileBadgeSize = 20.dp @Composable fun editTileColors(): TileColors = diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt index 26dfc7224ff9..3dfde86bf8d9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt @@ -32,23 +32,17 @@ fun rememberSelectionState(): MutableSelectionState { return remember { MutableSelectionState() } } -/** - * Holds the selected [TileSpec] and whether the selection was manual, i.e. caused by a tap from the - * user. - */ -data class Selection(val tileSpec: TileSpec, val manual: Boolean) - /** Holds the state of the current selection. */ class MutableSelectionState { - /** The [Selection] if a tile is selected, null if not. */ - var selection by mutableStateOf<Selection?>(null) + /** The [TileSpec] of a tile is selected, null if not. */ + var selection by mutableStateOf<TileSpec?>(null) private set val selected: Boolean get() = selection != null - fun select(tileSpec: TileSpec, manual: Boolean) { - selection = Selection(tileSpec, manual) + fun select(tileSpec: TileSpec) { + selection = tileSpec } fun unSelect() { @@ -68,10 +62,10 @@ fun Modifier.selectableTile( return pointerInput(Unit) { detectTapGestures( onTap = { - if (selectionState.selection?.tileSpec == tileSpec) { + if (selectionState.selection == tileSpec) { selectionState.unSelect() } else { - selectionState.select(tileSpec, manual = true) + selectionState.select(tileSpec) } onClick() } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt index 7c472638da63..699e5f6b77e9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt @@ -16,141 +16,232 @@ package com.android.systemui.qs.panels.ui.compose.selection -import androidx.compose.animation.core.Spring -import androidx.compose.animation.core.animateDpAsState -import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.animation.core.spring -import androidx.compose.foundation.Canvas +import androidx.compose.animation.animateColor +import androidx.compose.animation.core.Transition +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.animateOffset +import androidx.compose.animation.core.animateSize +import androidx.compose.animation.core.updateTransition import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.anchoredDraggable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.size import androidx.compose.foundation.systemGestureExclusion +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Remove +import androidx.compose.material3.Icon import androidx.compose.material3.LocalMinimumInteractiveComponentSize import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.State import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Rect +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.geometry.center import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.layout.layout +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.toSize import androidx.compose.ui.zIndex +import com.android.compose.modifiers.size import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius -import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.ResizingDotSize +import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.BADGE_ANGLE_RAD +import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.BadgeSize +import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.BadgeXOffset +import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.BadgeYOffset +import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.RESIZING_PILL_ANGLE_RAD +import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.ResizingPillHeight +import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.ResizingPillWidth import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.SelectedBorderWidth +import com.android.systemui.qs.panels.ui.compose.selection.TileState.None +import com.android.systemui.qs.panels.ui.compose.selection.TileState.Removable +import com.android.systemui.qs.panels.ui.compose.selection.TileState.Selected +import kotlin.math.cos import kotlin.math.roundToInt -import kotlinx.coroutines.launch +import kotlin.math.sin /** - * Places a dot to handle resizing drag events. Use this on tiles to resize. + * Draws a tile decoration and handles click and drag events for them. * - * The dot is placed vertically centered on the right border. The [content] will have a border when - * selected. + * In states: + * - [TileState.Removable]: removal icon shown in the top end + * - [TileState.Selected]: pill shaped handle shown on the end border, as well as a colored border + * around the content. + * - [TileState.None]: nothing * - * @param selected whether resizing drag events should be handled - * @param state the [ResizingState] for the tile - * @param selectionAlpha the animated value for the dot and border alpha - * @param selectionColor the [Color] of the dot and border + * @param tileState the state for the tile decoration + * @param resizingState the [ResizingState] for the tile + * @param onClick the callback when the tile decoration is clicked */ @Composable -fun ResizableTileContainer( - selected: Boolean, - state: ResizingState, - selectionAlpha: () -> Float, - selectionColor: Color, +fun InteractiveTileContainer( + tileState: TileState, + resizingState: ResizingState, modifier: Modifier = Modifier, + onClick: () -> Unit = {}, + onClickLabel: String? = null, content: @Composable BoxScope.() -> Unit = {}, ) { - Box(modifier.resizable(selected, state).selectionBorder(selectionColor, selectionAlpha)) { - content() - ResizingHandle( - enabled = selected, - state = state, - modifier = - // Higher zIndex to make sure the handle is drawn above the content - Modifier.zIndex(if (selected) 2f else 1f), - ) - } -} + val transition: Transition<TileState> = updateTransition(tileState) + val decorationColor by transition.animateColor() + val decorationAngle by transition.animateAngle() + val decorationSize by transition.animateSize() + val decorationOffset by transition.animateOffset() + val decorationAlpha by transition.animateFloat { state -> if (state == None) 0f else 1f } + val badgeIconAlpha by transition.animateFloat { state -> if (state == Removable) 1f else 0f } + val selectionBorderAlpha by + transition.animateFloat { state -> if (state == Selected) 1f else 0f } -@Composable -private fun ResizingHandle(enabled: Boolean, state: ResizingState, modifier: Modifier = Modifier) { - // Manually creating the touch target around the resizing dot to ensure that the next tile - // does not receive the touch input accidentally. - val minTouchTargetSize = LocalMinimumInteractiveComponentSize.current - val scope = rememberCoroutineScope() Box( - modifier - .layout { measurable, constraints -> - val size = minTouchTargetSize.roundToPx() - val placeable = measurable.measure(Constraints(size, size, size, size)) - layout(placeable.width, placeable.height) { - placeable.place( - x = constraints.maxWidth - placeable.width / 2, - y = constraints.maxHeight / 2 - placeable.height / 2, - ) - } - } - .systemGestureExclusion { Rect(Offset.Zero, it.size.toSize()) } - .anchoredDraggable( - enabled = enabled, - state = state.anchoredDraggableState, - orientation = Orientation.Horizontal, - ) - .clickable(enabled = enabled, interactionSource = null, indication = null) { - scope.launch { state.toggleCurrentValue() } - } + modifier.resizable(tileState == Selected, resizingState).selectionBorder( + MaterialTheme.colorScheme.primary, + SelectedBorderWidth, + ) { + selectionBorderAlpha + } ) { - ResizingDot(enabled = enabled, modifier = Modifier.align(Alignment.Center)) - } -} + content() -@Composable -private fun ResizingDot( - enabled: Boolean, - modifier: Modifier = Modifier, - color: Color = MaterialTheme.colorScheme.primary, -) { - val alpha by animateFloatAsState(if (enabled) 1f else 0f) - val radius by - animateDpAsState( - if (enabled) ResizingDotSize / 2 else 0.dp, - animationSpec = spring(dampingRatio = Spring.DampingRatioMediumBouncy), - ) - Canvas(modifier = modifier.size(ResizingDotSize)) { - drawCircle(color = color, radius = radius.toPx(), alpha = alpha) + MinimumInteractiveSizeComponent( + angle = { decorationAngle }, + offset = { decorationOffset }, + ) { + Box( + Modifier.fillMaxSize() + .drawBehind { + drawRoundRect( + color = decorationColor, + topLeft = center - decorationSize.center, + size = decorationSize, + cornerRadius = CornerRadius(size.width / 2), + ) + } + .graphicsLayer { this.alpha = decorationAlpha } + .anchoredDraggable( + enabled = tileState == Selected, + state = resizingState.anchoredDraggableState, + orientation = Orientation.Horizontal, + ) + .clickable( + enabled = tileState != None, + interactionSource = null, + indication = null, + onClickLabel = onClickLabel, + onClick = onClick, + ) + ) { + Icon( + Icons.Default.Remove, + contentDescription = null, + modifier = + Modifier.size( + width = { decorationSize.width.roundToInt() }, + height = { decorationSize.height.roundToInt() }, + ) + .align(Alignment.Center) + .graphicsLayer { this.alpha = badgeIconAlpha }, + ) + } + } } } private fun Modifier.selectionBorder( selectionColor: Color, + selectionBorderWidth: Dp, selectionAlpha: () -> Float = { 0f }, ): Modifier { return drawWithContent { drawContent() + + // Draw the border on the inside of the tile + val borderWidth = selectionBorderWidth.toPx() drawRoundRect( SolidColor(selectionColor), cornerRadius = CornerRadius(InactiveCornerRadius.toPx()), - style = Stroke(SelectedBorderWidth.toPx()), + topLeft = Offset(borderWidth / 2, borderWidth / 2), + size = Size(size.width - borderWidth, size.height - borderWidth), + style = Stroke(borderWidth), alpha = selectionAlpha(), ) } } @Composable +fun StaticTileBadge(icon: ImageVector, contentDescription: String?, onClick: () -> Unit) { + val offset = with(LocalDensity.current) { Offset(BadgeXOffset.toPx(), BadgeYOffset.toPx()) } + MinimumInteractiveSizeComponent(angle = { BADGE_ANGLE_RAD }, offset = { offset }) { + Box( + Modifier.fillMaxSize() + .clickable( + interactionSource = null, + indication = null, + onClickLabel = contentDescription, + onClick = onClick, + ) + ) { + val secondaryColor = MaterialTheme.colorScheme.secondary + Icon( + icon, + contentDescription = contentDescription, + modifier = + Modifier.size(BadgeSize).align(Alignment.Center).drawBehind { + drawCircle(secondaryColor) + }, + ) + } + } +} + +@Composable +private fun MinimumInteractiveSizeComponent( + angle: () -> Float, + offset: () -> Offset, + content: @Composable BoxScope.() -> Unit, +) { + // Use a higher zIndex than the tile to draw over it, and manually create the touch target + // as we're drawing over neighbor tiles as well. + val minTouchTargetSize = LocalMinimumInteractiveComponentSize.current + Box( + contentAlignment = Alignment.Center, + modifier = + Modifier.zIndex(2f) + .systemGestureExclusion { Rect(Offset.Zero, it.size.toSize()) } + .layout { measurable, constraints -> + val size = minTouchTargetSize.roundToPx() + val placeable = measurable.measure(Constraints.fixed(size, size)) + layout(placeable.width, placeable.height) { + val radius = constraints.maxHeight / 2f + val rotationCenter = Offset(constraints.maxWidth - radius, radius) + val position = offsetForAngle(angle(), radius, rotationCenter) + offset() + placeable.place( + position.x.roundToInt() - placeable.width / 2, + position.y.roundToInt() - placeable.height / 2, + ) + } + }, + content = content, + ) +} + +@Composable private fun Modifier.resizable(selected: Boolean, state: ResizingState): Modifier { if (!selected) return zIndex(1f) @@ -165,7 +256,69 @@ private fun Modifier.resizable(selected: Boolean, state: ResizingState): Modifie } } +enum class TileState { + None, + Removable, + Selected, +} + +@Composable +private fun Transition<TileState>.animateColor(): State<Color> { + return animateColor { state -> + when (state) { + None -> Color.Transparent + Removable -> MaterialTheme.colorScheme.secondary + Selected -> MaterialTheme.colorScheme.primary + } + } +} + +@Composable +private fun Transition<TileState>.animateAngle(): State<Float> { + return animateFloat { state -> + if (state == Removable) BADGE_ANGLE_RAD else RESIZING_PILL_ANGLE_RAD + } +} + +@Composable +private fun Transition<TileState>.animateSize(): State<Size> { + return animateSize { state -> + with(LocalDensity.current) { + when (state) { + None -> Size.Zero + Removable -> Size(BadgeSize.toPx()) + Selected -> Size(ResizingPillWidth.toPx(), ResizingPillHeight.toPx()) + } + } + } +} + +@Composable +private fun Transition<TileState>.animateOffset(): State<Offset> { + return animateOffset { state -> + with(LocalDensity.current) { + when (state) { + None -> Offset.Zero + Removable -> Offset(BadgeXOffset.toPx(), BadgeYOffset.toPx()) + Selected -> Offset(-SelectedBorderWidth.toPx(), 0f) + } + } + } +} + +private fun Size(size: Float) = Size(size, size) + +private fun offsetForAngle(angle: Float, radius: Float, center: Offset): Offset { + return Offset(x = radius * cos(angle) + center.x, y = radius * sin(angle) + center.y) +} + private object SelectionDefaults { - val ResizingDotSize = 16.dp val SelectedBorderWidth = 2.dp + val BadgeSize = 24.dp + val BadgeXOffset = -4.dp + val BadgeYOffset = 4.dp + val ResizingPillWidth = 8.dp + val ResizingPillHeight = 16.dp + const val BADGE_ANGLE_RAD = -.8f + const val RESIZING_PILL_ANGLE_RAD = 0f } diff --git a/packages/SystemUI/src/com/android/systemui/rotationlock/DeviceStateAutoRotateModule.kt b/packages/SystemUI/src/com/android/systemui/rotationlock/DeviceStateAutoRotateModule.kt new file mode 100644 index 000000000000..628280210236 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/rotationlock/DeviceStateAutoRotateModule.kt @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2025 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.rotationlock + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.policy.DeviceStateRotationLockSettingController +import com.android.window.flags.Flags +import dagger.Module +import dagger.Provides +import java.util.Optional +import javax.inject.Provider +import javax.inject.Qualifier + +@Module +class DeviceStateAutoRotateModule { + /** Qualifier for dependencies to be bound with [DeviceStateAutoRotateModule]. */ + @Qualifier + @MustBeDocumented + @Retention(AnnotationRetention.RUNTIME) + annotation class BoundsDeviceStateAutoRotateModule + + /** + * Provides an instance of [DeviceStateRotationLockSettingController]. + * + * @param controllerProvider The provider for [DeviceStateRotationLockSettingController]. + * @return An [Optional] containing the [DeviceStateRotationLockSettingController] instance if + * the `Flags.enableDeviceStateAutoRotateSettingRefactor()` flag is disabled, or an empty + * [Optional] otherwise. + */ + @Provides + @BoundsDeviceStateAutoRotateModule + @SysUISingleton + fun provideDeviceStateRotationLockSettingController( + controllerProvider: Provider<DeviceStateRotationLockSettingController> + ): Optional<DeviceStateRotationLockSettingController> = + if (Flags.enableDeviceStateAutoRotateSettingRefactor()) { + Optional.empty() + } else { + Optional.of(controllerProvider.get()) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java index 79a872edd2c5..bfd512fa6a2d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java @@ -607,13 +607,6 @@ public final class KeyboardShortcutListSearch { context.getString(R.string.group_system_lock_screen), Arrays.asList( Pair.create(KeyEvent.KEYCODE_L, KeyEvent.META_META_ON))), - /* Pull up Notes app for quick memo: Meta + Ctrl + N */ - new ShortcutKeyGroupMultiMappingInfo( - context.getString(R.string.group_system_quick_memo), - Arrays.asList( - Pair.create( - KeyEvent.KEYCODE_N, - KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON))), /* Access system settings: Meta + I */ new ShortcutKeyGroupMultiMappingInfo( context.getString(R.string.group_system_access_system_settings), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/AvalancheController.kt index dba8a9a6ddec..867db8ec8235 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/AvalancheController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/AvalancheController.kt @@ -163,6 +163,7 @@ constructor( if (entry in nextMap) nextMap.remove(entry) if (entry in nextList) nextList.remove(entry) + outcome = "add next" addToNext(entry, runnable) // Shorten headsUpEntryShowing display time @@ -174,7 +175,7 @@ constructor( headsUpEntryShowing!!.updateEntry( /* updatePostTime= */ false, /* updateEarliestRemovalTime= */ false, - /* reason= */ "avalanche duration update", + /* reason= */ "shorten duration of previously-last HUN", ) } } @@ -269,8 +270,10 @@ constructor( } nextList.sort() val entryList = showingList + nextList + val thisKey = getKey(entry) if (entryList.isEmpty()) { - log { "No avalanche HUNs, use default ms: $autoDismissMs" } + headsUpManagerLogger.logAvalancheDuration( + thisKey, autoDismissMs, "No avalanche HUNs, use default", nextKey = "") return autoDismissMs } // entryList.indexOf(entry) returns -1 even when the entry is in entryList @@ -281,27 +284,29 @@ constructor( } } if (thisEntryIndex == -1) { - log { "Untracked entry, use default ms: $autoDismissMs" } + headsUpManagerLogger.logAvalancheDuration( + thisKey, autoDismissMs, "Untracked entry, use default", nextKey = "") return autoDismissMs } val nextEntryIndex = thisEntryIndex + 1 - - // If last entry, use default duration if (nextEntryIndex >= entryList.size) { - log { "Last entry, use default ms: $autoDismissMs" } + headsUpManagerLogger.logAvalancheDuration( + thisKey, autoDismissMs, "Last entry, use default", nextKey = "") return autoDismissMs } val nextEntry = entryList[nextEntryIndex] + val nextKey = getKey(nextEntry) if (nextEntry.compareNonTimeFields(entry) == -1) { - // Next entry is higher priority - log { "Next entry is higher priority: 500ms" } + headsUpManagerLogger.logAvalancheDuration( + thisKey, 500, "LOWER priority than next: ", nextKey) return 500 } else if (nextEntry.compareNonTimeFields(entry) == 0) { - // Next entry is same priority - log { "Next entry is same priority: 1000ms" } + headsUpManagerLogger.logAvalancheDuration( + thisKey, 1000, "SAME priority as next: ", nextKey) return 1000 } else { - log { "Next entry is lower priority, use default ms: $autoDismissMs" } + headsUpManagerLogger.logAvalancheDuration( + thisKey, autoDismissMs, "HIGHER priority than next: ", nextKey) return autoDismissMs } } @@ -355,25 +360,28 @@ constructor( } private fun showNow(entry: HeadsUpEntry, runnableList: MutableList<Runnable>) { - log { "SHOW: " + getKey(entry) } - + headsUpManagerLogger.logAvalancheStage("show", getKey(entry)) uiEventLogger.log(ThrottleEvent.AVALANCHE_THROTTLING_HUN_SHOWN) headsUpEntryShowing = entry - runnableList.forEach { - if (it in debugRunnableLabelMap) { - log { "RUNNABLE: ${debugRunnableLabelMap[it]}" } + runnableList.forEach { runnable -> + if (debug) { + debugRunnableLabelMap[runnable]?.let { label -> + headsUpManagerLogger.logAvalancheStage("run", label) + // Remove label after logging to avoid memory leak + debugRunnableLabelMap.remove(runnable) + } } - it.run() + runnable.run() } } private fun showNext() { - log { "SHOW NEXT" } + headsUpManagerLogger.logAvalancheStage("show next", key = "") headsUpEntryShowing = null if (nextList.isEmpty()) { - log { "NO MORE TO SHOW" } + headsUpManagerLogger.logAvalancheStage("no more", key = "") previousHunKey = "" return } @@ -395,11 +403,7 @@ constructor( debugRunnableLabelMap.remove(r) } } - val queue = ArrayList<String>() - for (entry in listToDrop) { - queue.add("[${getKey(entry)}]") - } - val dropList = java.lang.String.join("\n", queue) + val dropList = listToDrop.joinToString("\n ") { getKey(it) } headsUpManagerLogger.logDroppedHuns(dropList) } clearNext() @@ -424,38 +428,24 @@ constructor( // Methods below are for logging only ========================================================== - private inline fun log(s: () -> String) { - if (debug) { - Log.d(tag, s()) - } - } - private fun getStateStr(): String { - return "\navalanche state:" + - "\n\tshowing: [${getKey(headsUpEntryShowing)}]" + - "\n\tprevious: [$previousHunKey]" + - "\n\tnext list: $nextListStr" + - "\n\tnext map: $nextMapStr" + - "\nBHUM.mHeadsUpEntryMap: " + - baseEntryMapStr() + return "\n[AC state]" + + "\nshow: ${getKey(headsUpEntryShowing)}" + + "\nprevious: $previousHunKey" + + "\n$nextStr" + + "\n[HeadsUpManagerImpl.mHeadsUpEntryMap] " + baseEntryMapStr() + "\n" } - private val nextListStr: String + private val nextStr: String get() { - val queue = ArrayList<String>() - for (entry in nextList) { - queue.add("[${getKey(entry)}]") + val nextListStr = nextList.joinToString("\n ") { getKey(it) } + if (nextList.toSet() == nextMap.keys.toSet()) { + return "next (${nextList.size}):\n $nextListStr" } - return java.lang.String.join("\n", queue) - } - - private val nextMapStr: String - get() { - val queue = ArrayList<String>() - for (entry in nextMap.keys) { - queue.add("[${getKey(entry)}]") - } - return java.lang.String.join("\n", queue) + // This should never happen + val nextMapStr = nextMap.keys.joinToString("\n ") { getKey(it) } + return "next list (${nextList.size}):\n $nextListStr" + + "\nnext map (${nextMap.size}):\n $nextMapStr" } fun getKey(entry: HeadsUpEntry?): String { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java index 7d74a496853f..87b9bf87c680 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java @@ -856,11 +856,11 @@ public class HeadsUpManagerImpl private String getEntryMapStr() { if (mHeadsUpEntryMap.isEmpty()) { - return "EMPTY"; + return ""; } StringBuilder entryMapStr = new StringBuilder(); for (HeadsUpEntry entry: mHeadsUpEntryMap.values()) { - entryMapStr.append("\n\t").append( + entryMapStr.append("\n ").append( entry.mEntry == null ? "null" : entry.mEntry.getKey()); } return entryMapStr.toString(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerLogger.kt index 65fb9ca558d7..388d357b3b15 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerLogger.kt @@ -71,7 +71,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { str3 = outcome bool1 = isEnabled }, - { "$str1\n\t=> AC[isEnabled:$bool1] update: $str2\n\t=> $str3" }, + { "$str1\n=> AC[enabled:$bool1] update: $str2\n=> $str3" }, ) } @@ -90,7 +90,33 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { str3 = outcome bool1 = isEnabled }, - { "$str1\n\t=> AC[isEnabled:$bool1] delete: $str2\n\t=> $str3" }, + { "$str1\n=> AC[enabled:$bool1] delete: $str2\n=> $str3" }, + ) + } + + fun logAvalancheStage(stage: String, key: String) { + buffer.log( + TAG, + INFO, + { + str1 = stage + str2 = key + }, + { "[AC] $str1 $str2" }, + ) + } + + fun logAvalancheDuration(thisKey: String, duration: Int, reason: String, nextKey: String) { + buffer.log( + TAG, + INFO, + { + str1 = thisKey + int1 = duration + str2 = reason + str3 = nextKey + }, + { "[AC] $str1 | $int1 ms | $str2 $str3" }, ) } @@ -325,7 +351,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { } fun logDroppedHuns(entryList: String) { - buffer.log(TAG, VERBOSE, { str1 = entryList }, { "[AC] Drop HUNs: $str1" }) + buffer.log(TAG, VERBOSE, { str1 = entryList }, { "[AC] dropped:\n $str1" }) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt index 54efa4a2bcf2..5c81c8e22bfc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt @@ -44,15 +44,18 @@ import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToPrimaryBouncerTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel +import com.android.systemui.keyguard.ui.viewmodel.AodToGlanceableHubTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.AodToGoneTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.AodToLockscreenTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.AodToOccludedTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.AodToPrimaryBouncerTransitionViewModel +import com.android.systemui.keyguard.ui.viewmodel.DozingToDreamingTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.DozingToGlanceableHubTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.DozingToLockscreenTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.DozingToOccludedTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.DozingToPrimaryBouncerTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel +import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToAodTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToLockscreenTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.GoneToAodTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.GoneToDozingTransitionViewModel @@ -135,7 +138,9 @@ constructor( private val aodToGoneTransitionViewModel: AodToGoneTransitionViewModel, private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel, private val aodToOccludedTransitionViewModel: AodToOccludedTransitionViewModel, + private val aodToGlanceableHubTransitionViewModel: AodToGlanceableHubTransitionViewModel, private val aodToPrimaryBouncerTransitionViewModel: AodToPrimaryBouncerTransitionViewModel, + private val dozingToDreamingTransitionViewModel: DozingToDreamingTransitionViewModel, dozingToGlanceableHubTransitionViewModel: DozingToGlanceableHubTransitionViewModel, private val dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel, private val dozingToOccludedTransitionViewModel: DozingToOccludedTransitionViewModel, @@ -144,6 +149,7 @@ constructor( private val dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel, private val glanceableHubToLockscreenTransitionViewModel: GlanceableHubToLockscreenTransitionViewModel, + private val glanceableHubToAodTransitionViewModel: GlanceableHubToAodTransitionViewModel, private val goneToAodTransitionViewModel: GoneToAodTransitionViewModel, private val goneToDozingTransitionViewModel: GoneToDozingTransitionViewModel, private val goneToDreamingTransitionViewModel: GoneToDreamingTransitionViewModel, @@ -571,7 +577,9 @@ constructor( aodToGoneTransitionViewModel.notificationAlpha(viewState), aodToLockscreenTransitionViewModel.notificationAlpha, aodToOccludedTransitionViewModel.lockscreenAlpha(viewState), + aodToGlanceableHubTransitionViewModel.lockscreenAlpha(viewState), aodToPrimaryBouncerTransitionViewModel.notificationAlpha, + dozingToDreamingTransitionViewModel.notificationAlpha, dozingToLockscreenTransitionViewModel.lockscreenAlpha, dozingToOccludedTransitionViewModel.lockscreenAlpha(viewState), dozingToPrimaryBouncerTransitionViewModel.notificationAlpha, @@ -591,6 +599,7 @@ constructor( offToLockscreenTransitionViewModel.lockscreenAlpha, primaryBouncerToLockscreenTransitionViewModel.lockscreenAlpha(viewState), glanceableHubToLockscreenTransitionViewModel.keyguardAlpha, + glanceableHubToAodTransitionViewModel.lockscreenAlpha, lockscreenToGlanceableHubTransitionViewModel.keyguardAlpha, ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java index de7215461c80..36193bd87ce2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java @@ -441,6 +441,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat mAnimationScheduler.addCallback(mAnimationCallback); mUserInfoController.addCallback(mOnUserInfoChangedListener); mStatusBarStateController.addCallback(mStatusBarStateListener); + mStatusBarState = mStatusBarStateController.getState(); mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback); mDisableStateTracker.startTracking(mCommandQueue, mView.getDisplay().getDisplayId()); if (mTintedIconManager == null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 6c8e1825ea0a..ba41fd4c40ab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -65,6 +65,7 @@ import com.android.systemui.shade.ShadeController; import com.android.systemui.shade.ShadeDisplayAware; import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor; import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor; +import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationClickNotifier; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -116,6 +117,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final static String TAG = "StatusBarNotificationActivityStarter"; private final Context mContext; + private final ShadeDialogContextInteractor mContextInteractor; private final Handler mMainThreadHandler; private final Executor mUiBgExecutor; @@ -156,6 +158,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit @Inject StatusBarNotificationActivityStarter( @ShadeDisplayAware Context context, + ShadeDialogContextInteractor contextInteractor, @Main Handler mainThreadHandler, @Background Executor uiBgExecutor, NotificationVisibilityProvider visibilityProvider, @@ -188,6 +191,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit PowerInteractor powerInteractor, UserTracker userTracker) { mContext = context; + mContextInteractor = contextInteractor; mMainThreadHandler = mainThreadHandler; mUiBgExecutor = uiBgExecutor; mVisibilityProvider = visibilityProvider; @@ -491,7 +495,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit boolean animate, boolean isActivityIntent) { mLogger.logStartNotificationIntent(entry); - final int displayId = mContext.getDisplayId(); + final int displayId = mContextInteractor.getContext().getDisplayId(); try { ActivityTransitionAnimator.Controller animationController = new StatusBarTransitionAnimatorController( @@ -532,7 +536,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit public void startNotificationGutsIntent(@NonNull final Intent intent, final int appUid, @NonNull ExpandableNotificationRow row) { boolean animate = mActivityStarter.shouldAnimateLaunch(true /* isActivityIntent */); - final int displayId = mContext.getDisplayId(); + final int displayId = mContextInteractor.getContext().getDisplayId(); ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() { @Override public boolean onDismiss() { @@ -571,7 +575,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit @Override public void startHistoryIntent(View view, boolean showHistory) { ModesEmptyShadeFix.assertInLegacyMode(); - final int displayId = mContext.getDisplayId(); + final int displayId = mContextInteractor.getContext().getDisplayId(); boolean animate = mActivityStarter.shouldAnimateLaunch(true /* isActivityIntent */); ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() { @Override @@ -621,7 +625,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit @Override public void startSettingsIntent(@NonNull View view, @NonNull SettingsIntent intentInfo) { - final int displayId = mContext.getDisplayId(); + final int displayId = mContextInteractor.getContext().getDisplayId(); boolean animate = mActivityStarter.shouldAnimateLaunch(true /* isActivityIntent */); ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileModel.kt index d3e37119fdcb..2433d112bc69 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileModel.kt @@ -27,6 +27,7 @@ import com.android.systemui.common.shared.model.Text import com.android.systemui.common.shared.model.Text.Companion.loadText import com.android.systemui.plugins.qs.QSTile import com.android.systemui.qs.tileimpl.QSTileImpl +import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon /** Model describing the state that the QS Internet tile should be in. */ sealed interface InternetTileModel { @@ -49,11 +50,14 @@ sealed interface InternetTileModel { state.contentDescription = contentDescription.loadContentDescription(context) // To support both SignalDrawable and other icons, give priority to icons over IDs - if (icon != null) { - state.icon = icon - } else if (iconId != null) { - state.icon = QSTileImpl.maybeLoadResourceIcon(iconId!!, context) - } + state.icon = + when { + icon is ResourceIcon -> + QSTileImpl.maybeLoadResourceIcon((icon as ResourceIcon).resId, context) + icon != null -> icon + iconId != null -> QSTileImpl.maybeLoadResourceIcon(iconId!!, context) + else -> null + } state.state = if (this is Active) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java index 88cf46a0ca07..b13e01be40f7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java @@ -29,7 +29,6 @@ import androidx.annotation.NonNull; import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager; import com.android.systemui.Dumpable; -import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.util.wrapper.RotationPolicyWrapper; @@ -43,7 +42,6 @@ import javax.inject.Inject; * Handles reading and writing of rotation lock settings per device state, as well as setting the * rotation lock when device state changes. */ -@SysUISingleton public final class DeviceStateRotationLockSettingController implements Listenable, RotationLockController.RotationLockControllerCallback, Dumpable { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java index 797aa1f3a3dd..3ee7f33e3d3a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java @@ -27,8 +27,10 @@ import androidx.annotation.NonNull; import com.android.internal.view.RotationPolicy.RotationPolicyListener; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.rotationlock.DeviceStateAutoRotateModule.BoundsDeviceStateAutoRotateModule; import com.android.systemui.util.wrapper.RotationPolicyWrapper; +import java.util.Optional; import java.util.concurrent.CopyOnWriteArrayList; import javax.inject.Inject; @@ -50,21 +52,25 @@ public final class RotationLockControllerImpl implements RotationLockController }; private final RotationPolicyWrapper mRotationPolicy; - private final DeviceStateRotationLockSettingController + private final Optional<DeviceStateRotationLockSettingController> mDeviceStateRotationLockSettingController; private final boolean mIsPerDeviceStateRotationLockEnabled; @Inject public RotationLockControllerImpl( RotationPolicyWrapper rotationPolicyWrapper, - DeviceStateRotationLockSettingController deviceStateRotationLockSettingController, + Optional<DeviceStateRotationLockSettingController> + deviceStateRotationLockSettingController, @Named(DEVICE_STATE_ROTATION_LOCK_DEFAULTS) String[] deviceStateRotationLockDefaults ) { mRotationPolicy = rotationPolicyWrapper; - mDeviceStateRotationLockSettingController = deviceStateRotationLockSettingController; mIsPerDeviceStateRotationLockEnabled = deviceStateRotationLockDefaults.length > 0; - if (mIsPerDeviceStateRotationLockEnabled) { - mCallbacks.add(mDeviceStateRotationLockSettingController); + mDeviceStateRotationLockSettingController = + deviceStateRotationLockSettingController; + + if (mIsPerDeviceStateRotationLockEnabled + && mDeviceStateRotationLockSettingController.isPresent()) { + mCallbacks.add(mDeviceStateRotationLockSettingController.get()); } setListening(true); @@ -113,8 +119,9 @@ public final class RotationLockControllerImpl implements RotationLockController } else { mRotationPolicy.unregisterRotationPolicyListener(mRotationPolicyListener); } - if (mIsPerDeviceStateRotationLockEnabled) { - mDeviceStateRotationLockSettingController.setListening(listening); + if (mIsPerDeviceStateRotationLockEnabled + && mDeviceStateRotationLockSettingController.isPresent()) { + mDeviceStateRotationLockSettingController.get().setListening(listening); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLogger.kt index 33ed419afef2..5dbe9b145bc7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLogger.kt @@ -21,29 +21,25 @@ import com.android.settingslib.notification.modes.ZenMode import com.android.systemui.qs.QSModesEvent import javax.inject.Inject -class ModesDialogEventLogger -@Inject -constructor( - private val uiEventLogger: UiEventLogger, -) { +class ModesDialogEventLogger @Inject constructor(private val uiEventLogger: UiEventLogger) { fun logModeOn(mode: ZenMode) { val id = if (mode.isManualDnd) QSModesEvent.QS_MODES_DND_ON else QSModesEvent.QS_MODES_MODE_ON - uiEventLogger.log(id, /* uid= */ 0, mode.rule.packageName) + uiEventLogger.log(id, /* uid= */ 0, mode.ownerPackage) } fun logModeOff(mode: ZenMode) { val id = if (mode.isManualDnd) QSModesEvent.QS_MODES_DND_OFF else QSModesEvent.QS_MODES_MODE_OFF - uiEventLogger.log(id, /* uid= */ 0, mode.rule.packageName) + uiEventLogger.log(id, /* uid= */ 0, mode.ownerPackage) } fun logModeSettings(mode: ZenMode) { val id = if (mode.isManualDnd) QSModesEvent.QS_MODES_DND_SETTINGS else QSModesEvent.QS_MODES_MODE_SETTINGS - uiEventLogger.log(id, /* uid= */ 0, mode.rule.packageName) + uiEventLogger.log(id, /* uid= */ 0, mode.ownerPackage) } fun logOpenDurationDialog(mode: ZenMode) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt index 07f1c3470c83..dc07202dc486 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt @@ -71,10 +71,10 @@ constructor( mode.id in prevIds -> true // Mode is enabled -> show if active (so user can toggle off), or if it // can be manually toggled on - mode.rule.isEnabled -> mode.isActive || mode.rule.isManualInvocationAllowed + mode.isEnabled -> mode.isActive || mode.isManualInvocationAllowed // Mode was created as disabled, or disabled by the app that owns it -> // will be shown with a "Not set" text - !mode.rule.isEnabled -> mode.status == ZenMode.Status.DISABLED_BY_OTHER + !mode.isEnabled -> mode.status == ZenMode.Status.DISABLED_BY_OTHER else -> false } } @@ -97,13 +97,13 @@ constructor( if (mode.isActive) R.string.zen_mode_on else R.string.zen_mode_off ), onClick = { - if (!mode.rule.isEnabled) { + if (!mode.isEnabled) { openSettings(mode) } else if (mode.isActive) { dialogEventLogger.logModeOff(mode) zenModeInteractor.deactivateMode(mode) } else { - if (mode.rule.isManualInvocationAllowed) { + if (mode.isManualInvocationAllowed) { if (zenModeInteractor.shouldAskForZenDuration(mode)) { dialogEventLogger.logOpenDurationDialog(mode) // NOTE: The dialog handles turning on the mode itself. @@ -144,10 +144,10 @@ constructor( * readers, and for the tile subtext will be augmented with the current status of the mode. */ private fun getModeDescription(mode: ZenMode, forAccessibility: Boolean): String? { - if (!mode.rule.isEnabled) { + if (!mode.isEnabled) { return context.resources.getString(R.string.zen_mode_set_up) } - if (!mode.rule.isManualInvocationAllowed && !mode.isActive) { + if (!mode.isManualInvocationAllowed && !mode.isActive) { return context.resources.getString(R.string.zen_mode_no_manual_invocation) } return if (forAccessibility) diff --git a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java index 32f2ca6fb696..367f54cf4936 100644 --- a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java +++ b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java @@ -147,7 +147,7 @@ public class CreateUserActivity extends Activity { super.onDestroy(); } - private void addUserNow(String userName, Drawable userIcon, Boolean isAdmin) { + private void addUserNow(String userName, Drawable userIcon, String iconPath, Boolean isAdmin) { mSetupUserDialog.dismiss(); userName = (userName == null || userName.trim().isEmpty()) ? getString(com.android.settingslib.R.string.user_new_user_name) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt index a0be02f1ef7e..bd4c5f50eee7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt @@ -43,6 +43,7 @@ import com.android.systemui.qs.panels.ui.compose.infinitegrid.DefaultEditTileGri import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.shared.model.TileCategory +import com.android.systemui.res.R import com.google.common.truth.Truth.assertThat import org.junit.Rule import org.junit.Test @@ -85,7 +86,9 @@ class ResizingTest : SysuiTestCase() { composeRule .onNodeWithContentDescription("tileA") - .performCustomAccessibilityActionWithLabel("Toggle size") + .performCustomAccessibilityActionWithLabel( + context.getString(R.string.accessibility_qs_edit_toggle_tile_size_action) + ) assertThat(tiles.find { it.tile.tileSpec.spec == "tileA" }?.width).isEqualTo(2) } @@ -101,7 +104,9 @@ class ResizingTest : SysuiTestCase() { composeRule .onNodeWithContentDescription("tileD_large") - .performCustomAccessibilityActionWithLabel("Toggle size") + .performCustomAccessibilityActionWithLabel( + context.getString(R.string.accessibility_qs_edit_toggle_tile_size_action) + ) assertThat(tiles.find { it.tile.tileSpec.spec == "tileD_large" }?.width).isEqualTo(1) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index 3190d3ae8f16..28eafa937097 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -76,6 +76,7 @@ import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.ShadeControllerImpl; import com.android.systemui.shade.data.repository.FakeShadeRepository; import com.android.systemui.shade.data.repository.ShadeAnimationRepository; +import com.android.systemui.shade.domain.interactor.FakeShadeDialogContextInteractor; import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor; import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl; import com.android.systemui.statusbar.CommandQueue; @@ -171,6 +172,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { private final FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); private ExpandableNotificationRow mNotificationRow; private ExpandableNotificationRow mBubbleNotificationRow; + private FakeShadeDialogContextInteractor mContextInteractor; private final Answer<Void> mCallOnDismiss = answerVoid( (OnDismissAction dismissAction, Runnable cancel, @@ -187,6 +189,8 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mDependency, TestableLooper.get(this)); + mContextInteractor = new FakeShadeDialogContextInteractor(mContext); + // Create standard notification with contentIntent mNotificationRow = notificationTestHelper.createRow(); StatusBarNotification sbn = mNotificationRow.getEntry().getSbn(); @@ -199,10 +203,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { bubbleSbn.getNotification().contentIntent = mContentIntent; bubbleSbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL; -// ArrayList<NotificationEntry> activeNotifications = new ArrayList<>(); -// activeNotifications.add(mNotificationRow.getEntry()); -// activeNotifications.add(mBubbleNotificationRow.getEntry()); -// when(mEntryManager.getVisibleNotifications()).thenReturn(activeNotifications); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); when(mOnUserInteractionCallback.registerFutureDismissal(eq(mNotificationRow.getEntry()), anyInt())).thenReturn(mFutureDismissalRunnable); @@ -232,6 +232,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mNotificationActivityStarter = new StatusBarNotificationActivityStarter( getContext(), + mContextInteractor, mHandler, mUiBgExecutor, mVisibilityProvider, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt index 0f21a16147f0..b8b2ec5a58ae 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt @@ -16,6 +16,7 @@ package com.android.systemui.communal.domain.interactor +import android.content.pm.UserInfo import android.content.testableContext import android.os.userManager import com.android.systemui.broadcast.broadcastDispatcher @@ -85,27 +86,28 @@ fun Kosmos.setCommunalV2ConfigEnabled(enabled: Boolean) { ) } -suspend fun Kosmos.setCommunalEnabled(enabled: Boolean) { +suspend fun Kosmos.setCommunalEnabled(enabled: Boolean): UserInfo { fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, enabled) - if (enabled) { + return if (enabled) { fakeUserRepository.asMainUser() } else { fakeUserRepository.asDefaultUser() } } -suspend fun Kosmos.setCommunalV2Enabled(enabled: Boolean) { +suspend fun Kosmos.setCommunalV2Enabled(enabled: Boolean): UserInfo { setCommunalV2ConfigEnabled(enabled) - setCommunalEnabled(enabled) + return setCommunalEnabled(enabled) } -suspend fun Kosmos.setCommunalAvailable(available: Boolean) { - setCommunalEnabled(available) +suspend fun Kosmos.setCommunalAvailable(available: Boolean): UserInfo { + val user = setCommunalEnabled(available) fakeKeyguardRepository.setKeyguardShowing(available) fakeUserRepository.setUserUnlocked(FakeUserRepository.MAIN_USER_ID, available) + return user } -suspend fun Kosmos.setCommunalV2Available(available: Boolean) { +suspend fun Kosmos.setCommunalV2Available(available: Boolean): UserInfo { setCommunalV2ConfigEnabled(available) - setCommunalAvailable(available) + return setCommunalAvailable(available) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt index 93a59eb8ca0c..bdfa875f5429 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt @@ -16,6 +16,9 @@ package com.android.systemui.keyguard.domain.interactor +import com.android.systemui.communal.domain.interactor.communalInteractor +import com.android.systemui.communal.domain.interactor.communalSceneInteractor +import com.android.systemui.communal.domain.interactor.communalSettingsInteractor import com.android.systemui.deviceentry.data.repository.deviceEntryRepository import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository import com.android.systemui.kosmos.Kosmos @@ -38,5 +41,8 @@ val Kosmos.fromAodTransitionInteractor by keyguardOcclusionInteractor = keyguardOcclusionInteractor, deviceEntryRepository = deviceEntryRepository, wakeToGoneInteractor = keyguardWakeDirectlyToGoneInteractor, + communalSettingsInteractor = communalSettingsInteractor, + communalSceneInteractor = communalSceneInteractor, + communalInteractor = communalInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToGlanceableHubTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToGlanceableHubTransitionViewModelKosmos.kt new file mode 100644 index 000000000000..1eeecd4b7520 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToGlanceableHubTransitionViewModelKosmos.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2025 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.keyguard.ui.viewmodel + +import com.android.systemui.keyguard.ui.glanceableHubBlurComponentFactory +import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow +import com.android.systemui.kosmos.Kosmos + +val Kosmos.aodToGlanceableHubTransitionViewModel by + Kosmos.Fixture { + AodToGlanceableHubTransitionViewModel( + animationFlow = keyguardTransitionAnimationFlow, + blurFactory = glanceableHubBlurComponentFactory, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelKosmos.kt index bd0045501ec8..2797b4409ff0 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelKosmos.kt @@ -47,5 +47,6 @@ val Kosmos.deviceEntryBackgroundViewModel by Fixture { primaryBouncerToLockscreenTransitionViewModel = primaryBouncerToLockscreenTransitionViewModel, lockscreenToDozingTransitionViewModel = lockscreenToDozingTransitionViewModel, + glanceableHubToAodTransitionViewModel = glanceableHubToAodTransitionViewModel, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToAodTransitionViewModelKosmos.kt new file mode 100644 index 000000000000..6004c7f2caec --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToAodTransitionViewModelKosmos.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2025 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.keyguard.ui.viewmodel + +import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor +import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow +import com.android.systemui.kosmos.Kosmos + +val Kosmos.glanceableHubToAodTransitionViewModel by + Kosmos.Fixture { + GlanceableHubToAodTransitionViewModel( + deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, + animationFlow = keyguardTransitionAnimationFlow, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt index 78356318cbb4..27ca0f867855 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt @@ -94,5 +94,7 @@ val Kosmos.keyguardRootViewModel by Fixture { shadeInteractor = shadeInteractor, wallpaperFocalAreaInteractor = wallpaperFocalAreaInteractor, dumpManager = dumpManager, + glanceableHubToAodTransitionViewModel = glanceableHubToAodTransitionViewModel, + aodToGlanceableHubTransitionViewModel = aodToGlanceableHubTransitionViewModel, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt index 7a2b7c24252b..51bb94fd2ab9 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt @@ -25,15 +25,18 @@ import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInterac import com.android.systemui.keyguard.ui.viewmodel.alternateBouncerToGoneTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.alternateBouncerToPrimaryBouncerTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.aodBurnInViewModel +import com.android.systemui.keyguard.ui.viewmodel.aodToGlanceableHubTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.aodToGoneTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.aodToLockscreenTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.aodToOccludedTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.aodToPrimaryBouncerTransitionViewModel +import com.android.systemui.keyguard.ui.viewmodel.dozingToDreamingTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.dozingToGlanceableHubTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.dozingToLockscreenTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.dozingToOccludedTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.dozingToPrimaryBouncerTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.dreamingToLockscreenTransitionViewModel +import com.android.systemui.keyguard.ui.viewmodel.glanceableHubToAodTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.glanceableHubToLockscreenTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.goneToAodTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.goneToDozingTransitionViewModel @@ -81,6 +84,7 @@ val Kosmos.sharedNotificationContainerViewModel by Fixture { aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel, aodToOccludedTransitionViewModel = aodToOccludedTransitionViewModel, aodToPrimaryBouncerTransitionViewModel = aodToPrimaryBouncerTransitionViewModel, + dozingToDreamingTransitionViewModel = dozingToDreamingTransitionViewModel, dozingToGlanceableHubTransitionViewModel = dozingToGlanceableHubTransitionViewModel, dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel, dozingToOccludedTransitionViewModel = dozingToOccludedTransitionViewModel, @@ -110,5 +114,7 @@ val Kosmos.sharedNotificationContainerViewModel by Fixture { headsUpNotificationInteractor = { headsUpNotificationInteractor }, largeScreenHeaderHelperLazy = { largeScreenHeaderHelper }, unfoldTransitionInteractor = unfoldTransitionInteractor, + glanceableHubToAodTransitionViewModel = glanceableHubToAodTransitionViewModel, + aodToGlanceableHubTransitionViewModel = aodToGlanceableHubTransitionViewModel, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt index d787e2c190c8..91404e0688ed 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt @@ -30,6 +30,7 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.plugins.activityStarter import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.settings.userTracker +import com.android.systemui.shade.data.repository.shadeDialogContextInteractor import com.android.systemui.shade.domain.interactor.panelExpansionInteractor import com.android.systemui.shade.domain.interactor.shadeAnimationInteractor import com.android.systemui.shade.shadeController @@ -52,6 +53,7 @@ val Kosmos.statusBarNotificationActivityStarter by Kosmos.Fixture { StatusBarNotificationActivityStarter( applicationContext, + shadeDialogContextInteractor, fakeExecutorHandler, fakeExecutor, notificationVisibilityProvider, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapterKosmos.kt index 4e3c3caa2428..d4d589663477 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapterKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapterKosmos.kt @@ -21,9 +21,9 @@ import com.android.systemui.statusbar.phone.ui.statusBarIconController import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.wifiViewModel val Kosmos.wifiUiAdapter by -Kosmos.Fixture { - WifiUiAdapter( + Kosmos.Fixture { + WifiUiAdapter( statusBarIconController, wifiViewModel, - ) -} + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelKosmos.kt index 15057cd5bd0c..3f876ef0536b 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelKosmos.kt @@ -24,14 +24,14 @@ import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.airplaneMod import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.wifiInteractor import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants -import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flowOf import org.mockito.kotlin.mock val Kosmos.wifiViewModel by - Kosmos.Fixture { + Kosmos.Fixture { WifiViewModel( airplaneModeViewModel, - { MutableStateFlow(false) }, + { flowOf(false) }, mock<ConnectivityConstants>(), applicationContext, logcatTableLogBuffer(this, "WifiViewModelTest"), @@ -39,4 +39,4 @@ val Kosmos.wifiViewModel by applicationCoroutineScope, mock<WifiConstants>(), ) - } + } diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java index 139bbae26289..93b4de856463 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -891,6 +891,11 @@ public class VirtualDeviceManagerService extends SystemService { } @Override + public VirtualDevice getVirtualDevice(int deviceId) { + return mImpl.getVirtualDevice(deviceId); + } + + @Override public long getDimDurationMillisForDeviceId(int deviceId) { VirtualDeviceImpl virtualDevice = getVirtualDeviceForId(deviceId); return virtualDevice == null ? -1 : virtualDevice.getDimDurationMillis(); diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index cce29592d912..125824c3f37e 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -618,7 +618,7 @@ public final class ActiveServices { Slog.i(TAG, " Stopping fg for service " + r); } setServiceForegroundInnerLocked(r, 0, null, 0, 0, - 0); + 0, /* systemRequestedTransition= */ true); } } @@ -1839,7 +1839,7 @@ public final class ActiveServices { ServiceRecord r = findServiceLocked(className, token, userId); if (r != null) { setServiceForegroundInnerLocked(r, id, notification, flags, foregroundServiceType, - callingUid); + callingUid, /* systemRequestedTransition= */ false); } } finally { mAm.mInjector.restoreCallingIdentity(origId); @@ -2155,7 +2155,7 @@ public final class ActiveServices { @GuardedBy("mAm") private void setServiceForegroundInnerLocked(final ServiceRecord r, int id, Notification notification, int flags, int foregroundServiceType, - int callingUidIfStart) { + int callingUidIfStart, boolean systemRequestedTransition) { if (id != 0) { if (notification == null) { throw new IllegalArgumentException("null notification"); @@ -2800,6 +2800,7 @@ public final class ActiveServices { // earlier. r.foregroundServiceType = 0; r.mFgsNotificationWasDeferred = false; + r.systemRequestedFgToBg = systemRequestedTransition; signalForegroundServiceObserversLocked(r); resetFgsRestrictionLocked(r); mAm.updateForegroundServiceUsageStats(r.name, r.userId, false); @@ -9339,14 +9340,22 @@ public final class ActiveServices { if (sr.foregroundServiceType == ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE && sr.foregroundId == notificationId) { - if (DEBUG_FOREGROUND_SERVICE) { - Slog.d(TAG, "Moving media service to foreground for package " - + packageName); + // check if service is explicitly requested by app to not be in foreground. + if (sr.systemRequestedFgToBg) { + Slog.d(TAG, + "System initiated service transition to foreground " + + "for package " + + packageName); + setServiceForegroundInnerLocked(sr, sr.foregroundId, + sr.foregroundNoti, /* flags */ 0, + ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, + /* callingUidStart */ 0, /* systemRequestedTransition */ true); + } else { + Slog.d(TAG, + "Ignoring system initiated foreground service transition for " + + "package" + + packageName); } - setServiceForegroundInnerLocked(sr, sr.foregroundId, - sr.foregroundNoti, /* flags */ 0, - ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, - /* callingUidStart */ 0); } } } @@ -9379,13 +9388,14 @@ public final class ActiveServices { if (sr.foregroundServiceType == ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK && sr.foregroundId == notificationId) { - if (DEBUG_FOREGROUND_SERVICE) { - Slog.d(TAG, "Forcing media foreground service to background for package " - + packageName); - } + Slog.d(TAG, + "System initiated transition of foreground service(type:media) to bg " + + "for package" + + packageName); setServiceForegroundInnerLocked(sr, /* id */ 0, /* notification */ null, /* flags */ 0, - /* foregroundServiceType */ 0, /* callingUidStart */ 0); + /* foregroundServiceType */ 0, /* callingUidStart */ 0, + /* systemRequestedTransition */ true); } } } diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index b677297dfef2..4bfee1d8398f 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -1464,7 +1464,10 @@ public class CachedAppOptimizer { void onProcessFrozen(ProcessRecord frozenProc) { if (useCompaction()) { synchronized (mProcLock) { - compactApp(frozenProc, CompactProfile.FULL, CompactSource.APP, false); + // only full-compact if process is cached + if (frozenProc.mState.getSetAdj() >= mCompactThrottleMinOomAdj) { + compactApp(frozenProc, CompactProfile.FULL, CompactSource.APP, false); + } } } frozenProc.onProcessFrozen(); diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index ca34a13c55b1..f443e4455734 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -158,6 +158,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN boolean fgWaiting; // is a timeout for going foreground already scheduled? boolean isNotAppComponentUsage; // is service binding not considered component/package usage? boolean isForeground; // is service currently in foreground mode? + boolean systemRequestedFgToBg; // system requested service to transition to background. boolean inSharedIsolatedProcess; // is the service in a shared isolated process int foregroundId; // Notification ID of last foreground req. Notification foregroundNoti; // Notification record of foreground state. diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java index d412277d2605..f5284a3ed589 100644 --- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java +++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java @@ -211,4 +211,20 @@ public abstract class VirtualDeviceManagerInternal { */ public abstract @NonNull VirtualDeviceManager.VirtualDevice createVirtualDevice( @NonNull VirtualDeviceParams params); + + /** + * Returns the details of the virtual device with the given ID, if any. + * + * <p>The returned object is a read-only representation of the virtual device that expose its + * properties.</p> + * + * <p>Note that if the virtual device is closed and becomes invalid, the returned object will + * not be updated and may contain stale values. Use a {@link VirtualDeviceListener} for real + * time updates of the availability of virtual devices.</p> + * + * @return the virtual device with the requested ID, or {@code null} if no such device exists or + * it has already been closed. + */ + @Nullable + public abstract VirtualDevice getVirtualDevice(int deviceId); } diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index 88f5c81231b8..c41b8db1ce75 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -333,7 +333,7 @@ public class AutomaticBrightnessController { int ambientLightHorizonLong, float userLux, float userNits, DisplayManagerFlags displayManagerFlags) { mInjector = injector; - mClock = injector.createClock(displayManagerFlags.offloadControlsDozeAutoBrightness()); + mClock = injector.createClock(); mContext = context; mCallbacks = callbacks; mSensorManager = sensorManager; @@ -1402,8 +1402,7 @@ public class AutomaticBrightnessController { public void onSensorChanged(SensorEvent event) { if (mLightSensorEnabled) { // The time received from the sensor is in nano seconds, hence changing it to ms - final long time = (mDisplayManagerFlags.offloadControlsDozeAutoBrightness()) - ? TimeUnit.NANOSECONDS.toMillis(event.timestamp) : mClock.uptimeMillis(); + final long time = TimeUnit.NANOSECONDS.toMillis(event.timestamp); final float lux = event.values[0]; handleLightSensorEvent(time, lux); } @@ -1616,20 +1615,13 @@ public class AutomaticBrightnessController { } private static class RealClock implements Clock { - private final boolean mOffloadControlsDozeBrightness; - - RealClock(boolean offloadControlsDozeBrightness) { - mOffloadControlsDozeBrightness = offloadControlsDozeBrightness; - } - @Override public long uptimeMillis() { return SystemClock.uptimeMillis(); } public long getSensorEventScaleTime() { - return (mOffloadControlsDozeBrightness) - ? SystemClock.elapsedRealtime() : uptimeMillis(); + return SystemClock.elapsedRealtime(); } } @@ -1638,8 +1630,8 @@ public class AutomaticBrightnessController { return BackgroundThread.getHandler(); } - Clock createClock(boolean offloadControlsDozeBrightness) { - return new RealClock(offloadControlsDozeBrightness); + Clock createClock() { + return new RealClock(); } } } diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java index 2c6f37448735..6510441ba28f 100644 --- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java +++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java @@ -291,8 +291,7 @@ public class DisplayBrightnessStrategySelector { void setAllowAutoBrightnessWhileDozing( DisplayManagerInternal.DisplayOffloadSession displayOffloadSession) { mAllowAutoBrightnessWhileDozing = mAllowAutoBrightnessWhileDozingConfig; - if (mDisplayManagerFlags.offloadControlsDozeAutoBrightness() - && mDisplayManagerFlags.isDisplayOffloadEnabled() + if (mDisplayManagerFlags.isDisplayOffloadEnabled() && displayOffloadSession != null) { mAllowAutoBrightnessWhileDozing &= displayOffloadSession.allowAutoBrightnessInDoze(); } diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java index e4b595ab7c55..c3057ded66eb 100644 --- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java +++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java @@ -156,11 +156,6 @@ public class DisplayManagerFlags { Flags.FLAG_DOZE_BRIGHTNESS_FLOAT, Flags::dozeBrightnessFloat); - private final FlagState mOffloadControlsDozeAutoBrightness = new FlagState( - Flags.FLAG_OFFLOAD_CONTROLS_DOZE_AUTO_BRIGHTNESS, - Flags::offloadControlsDozeAutoBrightness - ); - private final FlagState mPeakRefreshRatePhysicalLimit = new FlagState( Flags.FLAG_ENABLE_PEAK_REFRESH_RATE_PHYSICAL_LIMIT, Flags::enablePeakRefreshRatePhysicalLimit @@ -440,13 +435,6 @@ public class DisplayManagerFlags { return mDozeBrightnessFloat.isEnabled(); } - /** - * @return Whether DisplayOffload should control auto-brightness in doze - */ - public boolean offloadControlsDozeAutoBrightness() { - return mOffloadControlsDozeAutoBrightness.isEnabled(); - } - public boolean isPeakRefreshRatePhysicalLimitEnabled() { return mPeakRefreshRatePhysicalLimit.isEnabled(); } @@ -647,7 +635,6 @@ public class DisplayManagerFlags { pw.println(" " + mResolutionBackupRestore); pw.println(" " + mUseFusionProxSensor); pw.println(" " + mDozeBrightnessFloat); - pw.println(" " + mOffloadControlsDozeAutoBrightness); pw.println(" " + mPeakRefreshRatePhysicalLimit); pw.println(" " + mIgnoreAppPreferredRefreshRate); pw.println(" " + mSynthetic60hzModes); diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig index acdc0e0cf891..f5451307afb7 100644 --- a/services/core/java/com/android/server/display/feature/display_flags.aconfig +++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig @@ -255,17 +255,6 @@ flag { } flag { - name: "offload_controls_doze_auto_brightness" - namespace: "display_manager" - description: "Allows the registered DisplayOffloader to control if auto-brightness is used in doze" - bug: "327392714" - is_fixed_read_only: true - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "enable_peak_refresh_rate_physical_limit" namespace: "display_manager" description: "Flag for adding physical refresh rate limit if smooth display setting is on " diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index 7e8bb28b6a37..2af74f620c95 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -569,7 +569,8 @@ public final class DreamManagerService extends SystemService { } private void requestDreamInternal() { - if (isDreamingInternal() && !dreamIsFrontmost() && mController.bringDreamToFront()) { + if (isDreamingInternal() && !dreamIsFrontmost() && mController.bringDreamToFront() + && !isDozingInternal()) { return; } diff --git a/services/core/java/com/android/server/input/InputGestureManager.java b/services/core/java/com/android/server/input/InputGestureManager.java index 977c029f3a29..d71c8a1056d9 100644 --- a/services/core/java/com/android/server/input/InputGestureManager.java +++ b/services/core/java/com/android/server/input/InputGestureManager.java @@ -146,11 +146,6 @@ final class InputGestureManager { KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL ), createKeyGesture( - KeyEvent.KEYCODE_N, - KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES - ), - createKeyGesture( KeyEvent.KEYCODE_S, KeyEvent.META_META_ON, KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/OWNERS b/services/core/java/com/android/server/locksettings/recoverablekeystore/OWNERS index bb487fb52c9f..ebf7e6bed064 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/OWNERS +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/OWNERS @@ -1,4 +1,3 @@ aseemk@google.com bozhu@google.com dementyev@google.com -robertberry@google.com diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index 44d787f790cf..c31c287017c3 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -82,12 +82,10 @@ import android.util.LongSparseLongArray; import android.util.Slog; import android.util.SparseBooleanArray; -import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; import com.android.internal.infra.AndroidFuture; -import com.android.internal.policy.AttributeCache; import com.android.internal.util.IntPair; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.FgThread; @@ -167,6 +165,7 @@ public final class PermissionPolicyService extends SystemService { private Context mContext; private PackageManagerInternal mPackageManagerInternal; private PermissionManagerServiceInternal mPermissionManagerInternal; + private ActivityTaskManagerInternal mActivityTaskManagerInternal; private NotificationManagerInternal mNotificationManager; private TelephonyManager mTelephonyManager; private final KeyguardManager mKeyguardManager; @@ -189,6 +188,7 @@ public final class PermissionPolicyService extends SystemService { PackageManagerInternal.class); mPermissionManagerInternal = LocalServices.getService( PermissionManagerServiceInternal.class); + mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class); final IAppOpsService appOpsService = IAppOpsService.Stub.asInterface( ServiceManager.getService(Context.APP_OPS_SERVICE)); @@ -1154,7 +1154,7 @@ public final class PermissionPolicyService extends SystemService { activityInfo.packageName, info.getCallingPackage(), info.getIntent(), info.getCheckedOptions(), activityInfo.name, true) - || isNoDisplayActivity(activityInfo)) { + || isNoDisplayActivity(activityInfo, info.getUserId())) { return; } UserHandle user = UserHandle.of(taskInfo.userId); @@ -1170,9 +1170,7 @@ public final class PermissionPolicyService extends SystemService { }; private void onActivityManagerReady() { - ActivityTaskManagerInternal atm = - LocalServices.getService(ActivityTaskManagerInternal.class); - atm.registerActivityStartInterceptor( + mActivityTaskManagerInternal.registerActivityStartInterceptor( ActivityInterceptorCallback.PERMISSION_POLICY_ORDERED_ID, mActivityInterceptorCallback); } @@ -1227,20 +1225,14 @@ public final class PermissionPolicyService extends SystemService { null, activityName, false); } - private boolean isNoDisplayActivity(@NonNull ActivityInfo aInfo) { + private boolean isNoDisplayActivity(@NonNull ActivityInfo aInfo, int userId) { final int themeResource = aInfo.getThemeResource(); if (themeResource == Resources.ID_NULL) { return false; } - boolean noDisplay = false; - final AttributeCache.Entry ent = AttributeCache.instance() - .get(aInfo.packageName, themeResource, R.styleable.Window, 0); - if (ent != null) { - noDisplay = ent.array.getBoolean(R.styleable.Window_windowNoDisplay, false); - } - - return noDisplay; + return mActivityTaskManagerInternal.isNoDisplay(aInfo.packageName, themeResource, + userId); } /** diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 76c5240ab623..980fb155999e 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -1163,6 +1163,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + private boolean shouldShowHub() { + final boolean hubEnabled = Settings.Secure.getIntForUser( + mContext.getContentResolver(), Settings.Secure.GLANCEABLE_HUB_ENABLED, + 1, mCurrentUserId) == 1; + + return mUserManagerInternal != null && mUserManagerInternal.isUserUnlocked(mCurrentUserId) + && hubEnabled && mDreamManagerInternal.dreamConditionActive(); + } + @VisibleForTesting void powerPress(long eventTime, int count, int displayId) { // SideFPS still needs to know about suppressed power buttons, in case it needs to block @@ -1251,7 +1260,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { mContext.getContentResolver(), Settings.Secure.GLANCEABLE_HUB_ENABLED, 1, mCurrentUserId) == 1; - if (mDreamManagerInternal.isDreaming() || isKeyguardShowing()) { + if ((mDreamManagerInternal != null && mDreamManagerInternal.isDreaming()) + || isKeyguardShowing()) { // If the device is already dreaming or on keyguard, go to sleep. sleepDefaultDisplayFromPowerButton(eventTime, 0); break; @@ -1261,9 +1271,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { // show hub. boolean keyguardAvailable = !mLockPatternUtils.isLockScreenDisabled( mCurrentUserId); - if (mUserManagerInternal.isUserUnlocked(mCurrentUserId) && hubEnabled - && keyguardAvailable && mDreamManagerInternal.dreamConditionActive()) { - // If the hub can be launched, send a message to keyguard. + if (shouldShowHub() && keyguardAvailable) { + // If the hub can be launched, send a message to keyguard. We do not know if + // the hub is already running or not, keyguard handles turning screen off if + // it is. Bundle options = new Bundle(); options.putBoolean(EXTRA_TRIGGER_HUB, true); lockNow(options); @@ -1324,14 +1335,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { * @param isScreenOn Whether the screen is currently on. * @param noDreamAction The action to perform if dreaming is not possible. */ - private void attemptToDreamFromShortPowerButtonPress( + private boolean attemptToDreamFromShortPowerButtonPress( boolean isScreenOn, Runnable noDreamAction) { if (mShortPressOnPowerBehavior != SHORT_PRESS_POWER_DREAM_OR_SLEEP && mShortPressOnPowerBehavior != SHORT_PRESS_POWER_HUB_OR_DREAM_OR_SLEEP) { // If the power button behavior isn't one that should be able to trigger the dream, give // up. noDreamAction.run(); - return; + return false; } final DreamManagerInternal dreamManagerInternal = getDreamManagerInternal(); @@ -1339,7 +1350,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { Slog.d(TAG, "Can't start dreaming when attempting to dream from short power" + " press (isScreenOn=" + isScreenOn + ")"); noDreamAction.run(); - return; + return false; } synchronized (mLock) { @@ -1350,6 +1361,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { } dreamManagerInternal.requestDream(); + + return true; } /** @@ -2327,6 +2340,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { WindowWakeUpPolicy getWindowWakeUpPolicy() { return new WindowWakeUpPolicy(mContext); } + + DreamManagerInternal getDreamManagerInternal() { + return LocalServices.getService(DreamManagerInternal.class); + } } /** {@inheritDoc} */ @@ -2345,7 +2362,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class); mInputManager = mContext.getSystemService(InputManager.class); mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); - mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class); + mDreamManagerInternal = injector.getDreamManagerInternal(); mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); mAppOpsManager = mContext.getSystemService(AppOpsManager.class); mSensorPrivacyManager = mContext.getSystemService(SensorPrivacyManager.class); @@ -3701,15 +3718,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { break; case KeyEvent.KEYCODE_N: if (firstDown && event.isMetaPressed()) { - if (event.isCtrlPressed()) { - sendSystemKeyToStatusBarAsync(event); - notifyKeyGestureCompleted(event, - KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES); - } else { - toggleNotificationPanel(); - notifyKeyGestureCompleted(event, - KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL); - } + toggleNotificationPanel(); + notifyKeyGestureCompleted(event, + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL); return true; } break; @@ -6398,6 +6409,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { event.getDisplayId(), event.getKeyCode(), "wakeUpFromWakeKey")) { return; } + + if (!shouldShowHub() + && mShortPressOnPowerBehavior == SHORT_PRESS_POWER_HUB_OR_DREAM_OR_SLEEP + && event.getKeyCode() == KEYCODE_POWER + && attemptToDreamFromShortPowerButtonPress(false, () -> {})) { + // In the case that we should wake to dream and successfully initiate dreaming, do not + // continue waking up. Doing so will exit the dream state and cause UI to react + // accordingly. + return; + } + wakeUpFromWakeKey( event.getEventTime(), event.getKeyCode(), diff --git a/services/core/java/com/android/server/power/stats/format/ScreenPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/ScreenPowerStatsLayout.java index 6f6a7ff5064a..ca2237562fe1 100644 --- a/services/core/java/com/android/server/power/stats/format/ScreenPowerStatsLayout.java +++ b/services/core/java/com/android/server/power/stats/format/ScreenPowerStatsLayout.java @@ -29,7 +29,8 @@ import com.android.internal.os.PowerStats; public class ScreenPowerStatsLayout extends PowerStatsLayout { private static final String EXTRA_DEVICE_SCREEN_COUNT = "dsc"; private static final String EXTRA_DEVICE_SCREEN_ON_DURATION_POSITION = "dsd"; - private static final String EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS = "dbd"; + private static final String EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS = "dbds"; + private static final String EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS_COMPAT = "dbd"; private static final String EXTRA_DEVICE_DOZE_DURATION_POSITION = "ddd"; private static final String EXTRA_DEVICE_DOZE_POWER_POSITION = "ddp"; private static final String EXTRA_UID_FOREGROUND_DURATION = "uf"; @@ -55,8 +56,14 @@ public class ScreenPowerStatsLayout extends PowerStatsLayout { PersistableBundle extras = descriptor.extras; mDisplayCount = extras.getInt(EXTRA_DEVICE_SCREEN_COUNT, 1); mDeviceScreenOnDurationPosition = extras.getInt(EXTRA_DEVICE_SCREEN_ON_DURATION_POSITION); - mDeviceBrightnessDurationPositions = extras.getIntArray( + mDeviceBrightnessDurationPositions = getIntArray(extras, EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS); + if (mDeviceBrightnessDurationPositions == null) { + // We should never put arrays in PowerStats.Descriptor because of the performance of + // .equals + mDeviceBrightnessDurationPositions = extras.getIntArray( + EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS_COMPAT); + } mDeviceScreenDozeDurationPosition = extras.getInt(EXTRA_DEVICE_DOZE_DURATION_POSITION); mDeviceScreenDozePowerPosition = extras.getInt(EXTRA_DEVICE_DOZE_POWER_POSITION); mUidTopActivityTimePosition = extras.getInt(EXTRA_UID_FOREGROUND_DURATION); @@ -67,7 +74,7 @@ public class ScreenPowerStatsLayout extends PowerStatsLayout { super.toExtras(extras); extras.putInt(EXTRA_DEVICE_SCREEN_COUNT, mDisplayCount); extras.putInt(EXTRA_DEVICE_SCREEN_ON_DURATION_POSITION, mDeviceScreenOnDurationPosition); - extras.putIntArray(EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS, + putIntArray(extras, EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS, mDeviceBrightnessDurationPositions); extras.putInt(EXTRA_DEVICE_DOZE_DURATION_POSITION, mDeviceScreenDozeDurationPosition); extras.putInt(EXTRA_DEVICE_DOZE_POWER_POSITION, mDeviceScreenDozePowerPosition); diff --git a/services/core/java/com/android/server/power/stats/format/SensorPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/SensorPowerStatsLayout.java index e8df3ddfe5e8..c382534ac433 100644 --- a/services/core/java/com/android/server/power/stats/format/SensorPowerStatsLayout.java +++ b/services/core/java/com/android/server/power/stats/format/SensorPowerStatsLayout.java @@ -27,8 +27,10 @@ import java.util.Map; public class SensorPowerStatsLayout extends PowerStatsLayout { private static final String TAG = "SensorPowerStatsLayout"; - private static final String EXTRA_DEVICE_SENSOR_HANDLES = "dsh"; + private static final String EXTRA_DEVICE_SENSOR_HANDLES = "dshs"; + private static final String EXTRA_DEVICE_SENSOR_HANDLES_COMPAT = "dsh"; private static final String EXTRA_UID_SENSOR_POSITIONS = "usp"; + private static final String EXTRA_UID_SENSOR_POSITIONS_COMPAT = "usps"; private final SparseIntArray mSensorPositions = new SparseIntArray(); @@ -47,8 +49,14 @@ public class SensorPowerStatsLayout extends PowerStatsLayout { super(descriptor); PersistableBundle extras = descriptor.extras; - int[] handlers = extras.getIntArray(EXTRA_DEVICE_SENSOR_HANDLES); - int[] uidDurationPositions = extras.getIntArray(EXTRA_UID_SENSOR_POSITIONS); + int[] handlers = getIntArray(extras, EXTRA_DEVICE_SENSOR_HANDLES); + if (handlers == null) { + handlers = extras.getIntArray(EXTRA_DEVICE_SENSOR_HANDLES_COMPAT); + } + int[] uidDurationPositions = getIntArray(extras, EXTRA_UID_SENSOR_POSITIONS); + if (uidDurationPositions == null) { + uidDurationPositions = extras.getIntArray(EXTRA_UID_SENSOR_POSITIONS_COMPAT); + } if (handlers != null && uidDurationPositions != null) { for (int i = 0; i < handlers.length; i++) { @@ -69,8 +77,8 @@ public class SensorPowerStatsLayout extends PowerStatsLayout { uidDurationPositions[i] = mSensorPositions.valueAt(i); } - extras.putIntArray(EXTRA_DEVICE_SENSOR_HANDLES, handlers); - extras.putIntArray(EXTRA_UID_SENSOR_POSITIONS, uidDurationPositions); + putIntArray(extras, EXTRA_DEVICE_SENSOR_HANDLES, handlers); + putIntArray(extras, EXTRA_UID_SENSOR_POSITIONS, uidDurationPositions); } private void addUidSensorSection(int handle, String label) { diff --git a/services/core/java/com/android/server/power/stats/processor/AggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/processor/AggregatedPowerStats.java index a783d543559f..53894a196d24 100644 --- a/services/core/java/com/android/server/power/stats/processor/AggregatedPowerStats.java +++ b/services/core/java/com/android/server/power/stats/processor/AggregatedPowerStats.java @@ -25,6 +25,7 @@ import android.os.PersistableBundle; import android.os.UserHandle; import android.text.format.DateFormat; import android.util.IndentingPrintWriter; +import android.util.IntArray; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -347,7 +348,10 @@ class AggregatedPowerStats { Set<Integer> uids = new HashSet<>(); for (int i = 0; i < mPowerComponentStats.size(); i++) { - mPowerComponentStats.valueAt(i).collectUids(uids); + IntArray activeUids = mPowerComponentStats.valueAt(i).getActiveUids(); + for (int j = activeUids.size() - 1; j >= 0; j--) { + uids.add(activeUids.get(j)); + } } Integer[] allUids = uids.toArray(new Integer[uids.size()]); diff --git a/services/core/java/com/android/server/power/stats/processor/BasePowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/BasePowerStatsProcessor.java index d24ea83540cb..1d359335c6d8 100644 --- a/services/core/java/com/android/server/power/stats/processor/BasePowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/processor/BasePowerStatsProcessor.java @@ -24,13 +24,12 @@ import static com.android.server.power.stats.processor.AggregatedPowerStatsConfi import android.os.BatteryConsumer; import android.os.PersistableBundle; +import android.util.IntArray; import com.android.internal.os.PowerStats; import com.android.server.power.stats.format.BasePowerStatsLayout; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; import java.util.function.DoubleSupplier; class BasePowerStatsProcessor extends PowerStatsProcessor { @@ -125,11 +124,12 @@ class BasePowerStatsProcessor extends PowerStatsProcessor { mCumulativeDischargeUah = 0; mCumulativeDischargeDurationMs = 0; - List<Integer> uids = new ArrayList<>(); - stats.collectUids(uids); - - long durationMs = timestampMs - mStartTimestamp; - if (!uids.isEmpty()) { + // Note that we are calling `getUids` rather than `getActiveUids`, because this Processor + // deals with duration rather than power estimation, so it needs to process *all* known + // UIDs, not just the ones that contributed PowerStats + IntArray uids = stats.getUids(); + if (uids.size() != 0) { + long durationMs = timestampMs - mStartTimestamp; for (int i = uids.size() - 1; i >= 0; i--) { long[] uidStats = new long[sStatsLayout.getUidStatsArrayLength()]; sStatsLayout.setUidUsageDuration(uidStats, durationMs); diff --git a/services/core/java/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessor.java index 9fe7f3e7a542..c89dddf45609 100644 --- a/services/core/java/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessor.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.os.BatteryStats; import android.os.PersistableBundle; import android.os.Process; +import android.util.IntArray; import com.android.internal.os.PowerStats; import com.android.server.power.stats.UsageBasedPowerEstimator; @@ -27,7 +28,6 @@ import com.android.server.power.stats.format.BinaryStatePowerStatsLayout; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -190,13 +190,13 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor { } computeDevicePowerEstimates(stats, mPlan, mEnergyConsumerSupported); - combineDevicePowerEstimates(stats); - List<Integer> uids = new ArrayList<>(); - stats.collectUids(uids); - - computeUidActivityTotals(stats, uids); - computeUidPowerEstimates(stats, uids); + IntArray uids = stats.getActiveUids(); + if (uids.size() != 0) { + combineDevicePowerEstimates(stats); + computeUidActivityTotals(stats, uids); + computeUidPowerEstimates(stats, uids); + } } protected void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats, @@ -239,8 +239,7 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor { } } - private void computeUidActivityTotals(PowerComponentAggregatedPowerStats stats, - List<Integer> uids) { + private void computeUidActivityTotals(PowerComponentAggregatedPowerStats stats, IntArray uids) { for (int i = mPlan.uidStateEstimates.size() - 1; i >= 0; i--) { UidStateEstimate uidStateEstimate = mPlan.uidStateEstimates.get(i); Intermediates intermediates = @@ -259,8 +258,7 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor { } } - private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats, - List<Integer> uids) { + private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats, IntArray uids) { for (int i = mPlan.uidStateEstimates.size() - 1; i >= 0; i--) { UidStateEstimate uidStateEstimate = mPlan.uidStateEstimates.get(i); Intermediates intermediates = @@ -276,12 +274,13 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor { int uid = uids.get(k); if (stats.getUidStats(mTmpUidStatsArray, uid, proportionalEstimate.stateValues)) { - double power = intermediates.power - * mStatsLayout.getUidUsageDuration(mTmpUidStatsArray) - / intermediates.duration; - mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power); - stats.setUidStats(uid, proportionalEstimate.stateValues, - mTmpUidStatsArray); + long duration = mStatsLayout.getUidUsageDuration(mTmpUidStatsArray); + if (duration != 0) { + double power = intermediates.power * duration / intermediates.duration; + mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power); + stats.setUidStats(uid, proportionalEstimate.stateValues, + mTmpUidStatsArray); + } } } } diff --git a/services/core/java/com/android/server/power/stats/processor/BluetoothPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/BluetoothPowerStatsProcessor.java index 4c1a0db02273..c1cd3acf1656 100644 --- a/services/core/java/com/android/server/power/stats/processor/BluetoothPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/processor/BluetoothPowerStatsProcessor.java @@ -16,12 +16,13 @@ package com.android.server.power.stats.processor; +import android.util.IntArray; + import com.android.internal.os.PowerProfile; import com.android.internal.os.PowerStats; import com.android.server.power.stats.UsageBasedPowerEstimator; import com.android.server.power.stats.format.BluetoothPowerStatsLayout; -import java.util.ArrayList; import java.util.List; class BluetoothPowerStatsProcessor extends PowerStatsProcessor { @@ -118,18 +119,19 @@ class BluetoothPowerStatsProcessor extends PowerStatsProcessor { combineDeviceStateEstimates(); - ArrayList<Integer> uids = new ArrayList<>(); - stats.collectUids(uids); - if (!uids.isEmpty()) { - for (int uid : uids) { - for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) { - computeUidActivityTotals(stats, uid, mPlan.uidStateEstimates.get(i)); + IntArray uids = stats.getActiveUids(); + if (uids.size() != 0) { + for (int i = uids.size() - 1; i >= 0; i--) { + int uid = uids.get(i); + for (int j = 0; j < mPlan.uidStateEstimates.size(); j++) { + computeUidActivityTotals(stats, uid, mPlan.uidStateEstimates.get(j)); } } - for (int uid : uids) { - for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) { - computeUidPowerEstimates(stats, uid, mPlan.uidStateEstimates.get(i)); + for (int i = uids.size() - 1; i >= 0; i--) { + int uid = uids.get(i); + for (int j = 0; j < mPlan.uidStateEstimates.size(); j++) { + computeUidPowerEstimates(stats, uid, mPlan.uidStateEstimates.get(j)); } } } @@ -297,8 +299,10 @@ class BluetoothPowerStatsProcessor extends PowerStatsProcessor { / intermediates.txBytes; } } - mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power); - stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray); + if (power != 0) { + mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power); + stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray); + } } } } diff --git a/services/core/java/com/android/server/power/stats/processor/CpuPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/CpuPowerStatsProcessor.java index 17ceca6e3dc1..6ce2cf738192 100644 --- a/services/core/java/com/android/server/power/stats/processor/CpuPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/processor/CpuPowerStatsProcessor.java @@ -19,6 +19,7 @@ package com.android.server.power.stats.processor; import android.annotation.Nullable; import android.os.BatteryConsumer; import android.util.ArraySet; +import android.util.IntArray; import android.util.Log; import com.android.internal.os.CpuScalingPolicies; @@ -27,7 +28,6 @@ import com.android.internal.os.PowerStats; import com.android.server.power.stats.format.CpuPowerStatsLayout; import com.android.server.power.stats.format.WakelockPowerStatsLayout; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeUnit; @@ -189,12 +189,12 @@ class CpuPowerStatsProcessor extends PowerStatsProcessor { estimatePowerByDeviceState(stats, intermediates, wakelockStats); combineDeviceStateEstimates(); - ArrayList<Integer> uids = new ArrayList<>(); - stats.collectUids(uids); - if (!uids.isEmpty()) { - for (int uid : uids) { - for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) { - estimateUidPowerConsumption(stats, uid, mPlan.uidStateEstimates.get(i), + IntArray uids = stats.getActiveUids(); + if (uids.size() != 0) { + for (int i = uids.size() - 1; i >= 0; i--) { + int uid = uids.get(i); + for (int j = 0; j < mPlan.uidStateEstimates.size(); j++) { + estimateUidPowerConsumption(stats, uid, mPlan.uidStateEstimates.get(j), wakelockStats); } } @@ -545,8 +545,10 @@ class CpuPowerStatsProcessor extends PowerStatsProcessor { power = Math.max(0, power - wakelockPowerEstimate); } - mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power); - stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray); + if (power != 0) { + mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power); + stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray); + } } } } diff --git a/services/core/java/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsProcessor.java index 76adc47cc165..76ea7e841106 100644 --- a/services/core/java/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsProcessor.java @@ -16,10 +16,11 @@ package com.android.server.power.stats.processor; +import android.util.IntArray; + import com.android.internal.os.PowerStats; import com.android.server.power.stats.format.EnergyConsumerPowerStatsLayout; -import java.util.ArrayList; import java.util.List; class CustomEnergyConsumerPowerStatsProcessor extends PowerStatsProcessor { @@ -40,10 +41,8 @@ class CustomEnergyConsumerPowerStatsProcessor extends PowerStatsProcessor { computeDevicePowerEstimates(stats); - List<Integer> uids = new ArrayList<>(); - stats.collectUids(uids); - - if (!uids.isEmpty()) { + IntArray uids = stats.getActiveUids(); + if (uids.size() != 0) { computeUidPowerEstimates(stats, uids); } } @@ -62,7 +61,7 @@ class CustomEnergyConsumerPowerStatsProcessor extends PowerStatsProcessor { } private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats, - List<Integer> uids) { + IntArray uids) { for (int i = mPlan.uidStateEstimates.size() - 1; i >= 0; i--) { UidStateEstimate uidStateEstimate = mPlan.uidStateEstimates.get(i); List<UidStateProportionalEstimate> proportionalEstimates = @@ -73,9 +72,12 @@ class CustomEnergyConsumerPowerStatsProcessor extends PowerStatsProcessor { int uid = uids.get(k); if (stats.getUidStats(mTmpUidStatsArray, uid, proportionalEstimate.stateValues)) { - sLayout.setUidPowerEstimate(mTmpUidStatsArray, - uCtoMah(sLayout.getUidConsumedEnergy(mTmpUidStatsArray, 0))); - stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray); + double power = uCtoMah(sLayout.getUidConsumedEnergy(mTmpUidStatsArray, 0)); + if (power != 0) { + sLayout.setUidPowerEstimate(mTmpUidStatsArray, power); + stats.setUidStats(uid, proportionalEstimate.stateValues, + mTmpUidStatsArray); + } } } } diff --git a/services/core/java/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessor.java index b4c40de862b4..a544daad82f1 100644 --- a/services/core/java/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessor.java @@ -19,6 +19,7 @@ import android.os.BatteryStats; import android.telephony.CellSignalStrength; import android.telephony.ModemActivityInfo; import android.telephony.ServiceState; +import android.util.IntArray; import android.util.Log; import android.util.Slog; import android.util.SparseArray; @@ -29,7 +30,6 @@ import com.android.internal.power.ModemPowerProfile; import com.android.server.power.stats.UsageBasedPowerEstimator; import com.android.server.power.stats.format.MobileRadioPowerStatsLayout; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -198,18 +198,19 @@ class MobileRadioPowerStatsProcessor extends PowerStatsProcessor { combineDeviceStateEstimates(); - ArrayList<Integer> uids = new ArrayList<>(); - stats.collectUids(uids); - if (!uids.isEmpty()) { - for (int uid : uids) { - for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) { - computeUidRxTxTotals(stats, uid, mPlan.uidStateEstimates.get(i)); + IntArray uids = stats.getActiveUids(); + if (uids.size() != 0) { + for (int i = uids.size() - 1; i >= 0; i--) { + int uid = uids.get(i); + for (int j = 0; j < mPlan.uidStateEstimates.size(); j++) { + computeUidRxTxTotals(stats, uid, mPlan.uidStateEstimates.get(j)); } } - for (int uid : uids) { - for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) { - computeUidPowerEstimates(stats, uid, mPlan.uidStateEstimates.get(i)); + for (int i = uids.size() - 1; i >= 0; i--) { + int uid = uids.get(i); + for (int j = 0; j < mPlan.uidStateEstimates.size(); j++) { + computeUidPowerEstimates(stats, uid, mPlan.uidStateEstimates.get(j)); } } } @@ -382,8 +383,10 @@ class MobileRadioPowerStatsProcessor extends PowerStatsProcessor { / intermediates.txPackets; } - mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power); - stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray); + if (power != 0) { + mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power); + stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray); + } if (DEBUG) { Slog.d(TAG, "UID: " + uid diff --git a/services/core/java/com/android/server/power/stats/processor/MultiStateStats.java b/services/core/java/com/android/server/power/stats/processor/MultiStateStats.java index 28474a554b38..69325757c79d 100644 --- a/services/core/java/com/android/server/power/stats/processor/MultiStateStats.java +++ b/services/core/java/com/android/server/power/stats/processor/MultiStateStats.java @@ -16,6 +16,7 @@ package com.android.server.power.stats.processor; +import android.annotation.Nullable; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; @@ -333,9 +334,9 @@ class MultiStateStats { /** * Adds the delta to the metrics. The number of values must correspond to the dimension count - * supplied to the Factory constructor + * supplied to the Factory constructor. Null values is equivalent to an array of zeros. */ - void increment(long[] values, long timestampMs) { + void increment(@Nullable long[] values, long timestampMs) { mCounter.incrementValues(values, timestampMs); mTracking = true; } diff --git a/services/core/java/com/android/server/power/stats/processor/PowerComponentAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/processor/PowerComponentAggregatedPowerStats.java index d4f8fd92fc6c..f9b9da9171cb 100644 --- a/services/core/java/com/android/server/power/stats/processor/PowerComponentAggregatedPowerStats.java +++ b/services/core/java/com/android/server/power/stats/processor/PowerComponentAggregatedPowerStats.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.os.BatteryStats; import android.os.UserHandle; import android.util.IndentingPrintWriter; +import android.util.IntArray; import android.util.Slog; import android.util.SparseArray; @@ -34,7 +35,6 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.StringWriter; import java.util.Arrays; -import java.util.Collection; import java.util.function.IntConsumer; /** @@ -72,11 +72,11 @@ class PowerComponentAggregatedPowerStats { private MultiStateStats mDeviceStats; private final SparseArray<MultiStateStats> mStateStats = new SparseArray<>(); private final SparseArray<UidStats> mUidStats = new SparseArray<>(); - private long[] mZeroArray; private static class UidStats { public int[] states; public MultiStateStats stats; + public boolean hasPowerStats; public boolean updated; } @@ -200,6 +200,7 @@ class PowerComponentAggregatedPowerStats { if (uidStats.stats == null) { createUidStats(uidStats, mPowerStatsTimestamp); } + uidStats.hasPowerStats = true; uidStats.stats.setStats(states, values); } @@ -240,6 +241,7 @@ class PowerComponentAggregatedPowerStats { } uidStats.stats.increment(powerStats.uidStats.valueAt(i), timestampMs); uidStats.updated = true; + uidStats.hasPowerStats = true; } // For UIDs not mentioned in the PowerStats object, we must assume a 0 increment. @@ -248,11 +250,8 @@ class PowerComponentAggregatedPowerStats { for (int i = mUidStats.size() - 1; i >= 0; i--) { PowerComponentAggregatedPowerStats.UidStats uidStats = mUidStats.valueAt(i); if (!uidStats.updated && uidStats.stats != null) { - if (mZeroArray == null - || mZeroArray.length != mPowerStatsDescriptor.uidStatsArrayLength) { - mZeroArray = new long[mPowerStatsDescriptor.uidStatsArrayLength]; - } - uidStats.stats.increment(mZeroArray, timestampMs); + // Null stands for an array of zeros + uidStats.stats.increment(null, timestampMs); } uidStats.updated = false; } @@ -267,6 +266,7 @@ class PowerComponentAggregatedPowerStats { mStateStats.clear(); for (int i = mUidStats.size() - 1; i >= 0; i--) { mUidStats.valueAt(i).stats = null; + mUidStats.valueAt(i).hasPowerStats = false; } } @@ -290,12 +290,26 @@ class PowerComponentAggregatedPowerStats { return uidStats; } - void collectUids(Collection<Integer> uids) { + IntArray getUids() { + IntArray uids = new IntArray(mUidStats.size()); + for (int i = mUidStats.size() - 1; i >= 0; i--) { + UidStats uidStats = mUidStats.valueAt(i); + if (uidStats.stats != null) { + uids.add(mUidStats.keyAt(i)); + } + } + return uids; + } + + IntArray getActiveUids() { + IntArray uids = new IntArray(mUidStats.size()); for (int i = mUidStats.size() - 1; i >= 0; i--) { - if (mUidStats.valueAt(i).stats != null) { + UidStats uidStats = mUidStats.valueAt(i); + if (uidStats.hasPowerStats) { uids.add(mUidStats.keyAt(i)); } } + return uids; } boolean getDeviceStats(long[] outValues, int[] deviceStates) { @@ -516,6 +530,7 @@ class PowerComponentAggregatedPowerStats { if (uidStats.stats == null) { createUidStats(uidStats, UNKNOWN); } + uidStats.hasPowerStats = true; if (!uidStats.stats.readFromXml(parser)) { return false; } diff --git a/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java b/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java index 177d12988a27..634415ece806 100644 --- a/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java +++ b/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java @@ -21,6 +21,7 @@ import android.os.AggregateBatteryConsumer; import android.os.BatteryConsumer; import android.os.BatteryUsageStats; import android.os.UidBatteryConsumer; +import android.util.IntArray; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; @@ -31,7 +32,6 @@ import com.android.server.power.stats.PowerStatsStore; import com.android.server.power.stats.format.BasePowerStatsLayout; import com.android.server.power.stats.format.PowerStatsLayout; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeUnit; @@ -180,8 +180,7 @@ class PowerStatsExporter { } } if (layout.isUidPowerAttributionSupported()) { - populateBatteryConsumers(batteryUsageStatsBuilder, - powerComponentStats, layout); + populateBatteryConsumers(batteryUsageStatsBuilder, powerComponentStats, layout); } populateBatteryLevelInfo(batteryUsageStatsBuilder, batteryLevelInfo); @@ -258,6 +257,11 @@ class PowerStatsExporter { BatteryUsageStats.Builder batteryUsageStatsBuilder, PowerComponentAggregatedPowerStats powerComponentStats, PowerStatsLayout layout) { + IntArray uids = powerComponentStats.getUids(); + if (uids.size() == 0) { + return; + } + AggregatedPowerStatsConfig.PowerComponent powerComponent = powerComponentStats.getConfig(); PowerStats.Descriptor descriptor = powerComponentStats.getPowerStatsDescriptor(); long[] uidStats = new long[descriptor.uidStatsArrayLength]; @@ -273,8 +277,6 @@ class PowerStatsExporter { breakDownByProcState = false; } - ArrayList<Integer> uids = new ArrayList<>(); - powerComponentStats.collectUids(uids); for (int screenState = 0; screenState < BatteryConsumer.SCREEN_STATE_COUNT; screenState++) { if (batteryUsageStatsBuilder.isScreenStateDataNeeded()) { if (screenState == BatteryConsumer.SCREEN_STATE_UNSPECIFIED) { @@ -303,7 +305,7 @@ class PowerStatsExporter { private void populateUidBatteryConsumers( BatteryUsageStats.Builder batteryUsageStatsBuilder, PowerComponentAggregatedPowerStats powerComponentStats, PowerStatsLayout layout, - List<Integer> uids, AggregatedPowerStatsConfig.PowerComponent powerComponent, + IntArray uids, AggregatedPowerStatsConfig.PowerComponent powerComponent, long[] uidStats, boolean breakDownByProcState, @BatteryConsumer.ScreenState int screenState, @BatteryConsumer.PowerState int powerState) { @@ -319,7 +321,8 @@ class PowerStatsExporter { long[] durationByProcState = new long[breakDownByProcState ? BatteryConsumer.PROCESS_STATE_COUNT : 1]; double powerAllApps = 0; - for (int uid : uids) { + for (int i = uids.size() - 1; i >= 0; i--) { + int uid = uids.get(i); UidBatteryConsumer.Builder builder = batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uid); diff --git a/services/core/java/com/android/server/power/stats/processor/ScreenPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/ScreenPowerStatsProcessor.java index 8e7498f38fcb..9df3d7eea27b 100644 --- a/services/core/java/com/android/server/power/stats/processor/ScreenPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/processor/ScreenPowerStatsProcessor.java @@ -27,6 +27,7 @@ import static com.android.server.power.stats.processor.AggregatedPowerStatsConfi import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN; import android.os.BatteryStats; +import android.util.IntArray; import android.util.Slog; import com.android.internal.os.PowerProfile; @@ -34,7 +35,6 @@ import com.android.internal.os.PowerStats; import com.android.server.power.stats.UsageBasedPowerEstimator; import com.android.server.power.stats.format.ScreenPowerStatsLayout; -import java.util.ArrayList; import java.util.List; class ScreenPowerStatsProcessor extends PowerStatsProcessor { @@ -116,10 +116,8 @@ class ScreenPowerStatsProcessor extends PowerStatsProcessor { computeDevicePowerEstimates(stats); combineDeviceStateEstimates(); - List<Integer> uids = new ArrayList<>(); - stats.collectUids(uids); - - if (!uids.isEmpty()) { + IntArray uids = stats.getActiveUids(); + if (uids.size() != 0) { computeUidPowerEstimates(stats, uids); } mPlan.resetIntermediates(); @@ -197,7 +195,7 @@ class ScreenPowerStatsProcessor extends PowerStatsProcessor { } private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats, - List<Integer> uids) { + IntArray uids) { int[] uidStateValues = new int[stats.getConfig().getUidStateConfig().length]; uidStateValues[STATE_SCREEN] = SCREEN_STATE_ON; uidStateValues[STATE_PROCESS_STATE] = PROCESS_STATE_UNSPECIFIED; @@ -232,9 +230,11 @@ class ScreenPowerStatsProcessor extends PowerStatsProcessor { int uid = uids.get(j); if (stats.getUidStats(mTmpUidStatsArray, uid, uidStateValues)) { long duration = mStatsLayout.getUidTopActivityDuration(mTmpUidStatsArray); - double power = intermediates.power * duration / totalTopActivityDuration; - mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power); - stats.setUidStats(uid, uidStateValues, mTmpUidStatsArray); + if (duration != 0) { + double power = intermediates.power * duration / totalTopActivityDuration; + mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power); + stats.setUidStats(uid, uidStateValues, mTmpUidStatsArray); + } } } } diff --git a/services/core/java/com/android/server/power/stats/processor/SensorPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/SensorPowerStatsProcessor.java index 0bb028bce5af..ba728d36d561 100644 --- a/services/core/java/com/android/server/power/stats/processor/SensorPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/processor/SensorPowerStatsProcessor.java @@ -21,6 +21,7 @@ import android.hardware.SensorManager; import android.os.BatteryConsumer; import android.os.BatteryStats; import android.os.PersistableBundle; +import android.util.IntArray; import android.util.Slog; import android.util.SparseArray; @@ -28,7 +29,6 @@ import com.android.internal.os.PowerStats; import com.android.server.power.stats.format.PowerStatsLayout; import com.android.server.power.stats.format.SensorPowerStatsLayout; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.function.Supplier; @@ -207,11 +207,11 @@ class SensorPowerStatsProcessor extends PowerStatsProcessor { mPlan = new PowerEstimationPlan(stats.getConfig()); } - List<Integer> uids = new ArrayList<>(); - stats.collectUids(uids); - - computeUidPowerEstimates(stats, uids); - computeDevicePowerEstimates(stats); + IntArray uids = stats.getActiveUids(); + if (uids.size() != 0) { + computeUidPowerEstimates(stats, uids); + computeDevicePowerEstimates(stats); + } mPlan.resetIntermediates(); } @@ -239,9 +239,7 @@ class SensorPowerStatsProcessor extends PowerStatsProcessor { mLastUpdateTimestamp = timestamp; } - private void computeUidPowerEstimates( - PowerComponentAggregatedPowerStats stats, - List<Integer> uids) { + private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats, IntArray uids) { List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL); int[] uidSensorDurationPositions = new int[sensorList.size()]; double[] sensorPower = new double[sensorList.size()]; @@ -292,8 +290,7 @@ class SensorPowerStatsProcessor extends PowerStatsProcessor { } } - private void computeDevicePowerEstimates( - PowerComponentAggregatedPowerStats stats) { + private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats) { for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) { CombinedDeviceStateEstimate estimation = mPlan.combinedDeviceStateEstimations.get(i); diff --git a/services/core/java/com/android/server/power/stats/processor/WifiPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/WifiPowerStatsProcessor.java index 0df01cf7e5d1..8cc0b6eb6150 100644 --- a/services/core/java/com/android/server/power/stats/processor/WifiPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/processor/WifiPowerStatsProcessor.java @@ -16,6 +16,7 @@ package com.android.server.power.stats.processor; +import android.util.IntArray; import android.util.Slog; import com.android.internal.os.PowerProfile; @@ -23,7 +24,6 @@ import com.android.internal.os.PowerStats; import com.android.server.power.stats.UsageBasedPowerEstimator; import com.android.server.power.stats.format.WifiPowerStatsLayout; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -148,18 +148,19 @@ class WifiPowerStatsProcessor extends PowerStatsProcessor { combineDeviceStateEstimates(); - ArrayList<Integer> uids = new ArrayList<>(); - stats.collectUids(uids); - if (!uids.isEmpty()) { - for (int uid : uids) { - for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) { - computeUidActivityTotals(stats, uid, mPlan.uidStateEstimates.get(i)); + IntArray uids = stats.getActiveUids(); + if (uids.size() != 0) { + for (int i = uids.size() - 1; i >= 0; i--) { + int uid = uids.get(i); + for (int j = 0; j < mPlan.uidStateEstimates.size(); j++) { + computeUidActivityTotals(stats, uid, mPlan.uidStateEstimates.get(j)); } } - for (int uid : uids) { - for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) { - computeUidPowerEstimates(stats, uid, mPlan.uidStateEstimates.get(i)); + for (int i = uids.size() - 1; i >= 0; i--) { + int uid = uids.get(i); + for (int j = 0; j < mPlan.uidStateEstimates.size(); j++) { + computeUidPowerEstimates(stats, uid, mPlan.uidStateEstimates.get(j)); } } } @@ -374,8 +375,10 @@ class WifiPowerStatsProcessor extends PowerStatsProcessor { / intermediates.batchedScanDuration; } } - mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power); - stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray); + if (power != 0) { + mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power); + stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray); + } if (DEBUG) { Slog.d(TAG, "UID: " + uid diff --git a/services/core/java/com/android/server/vibrator/VendorVibrationSession.java b/services/core/java/com/android/server/vibrator/VendorVibrationSession.java index 1b6ce9dacfa9..64b52b175252 100644 --- a/services/core/java/com/android/server/vibrator/VendorVibrationSession.java +++ b/services/core/java/com/android/server/vibrator/VendorVibrationSession.java @@ -176,14 +176,14 @@ final class VendorVibrationSession extends IVibrationSession.Stub @Override public void onCancel() { - Slog.d(TAG, "Cancellation signal received, cancelling vibration session..."); + Slog.d(TAG, "Session cancellation signal received, aborting vibration session..."); requestEndSession(Status.CANCELLED_BY_USER, /* shouldAbort= */ true, /* isVendorRequest= */ true); } @Override public void binderDied() { - Slog.d(TAG, "Binder died, cancelling vibration session..."); + Slog.d(TAG, "Session binder died, aborting vibration session..."); requestEndSession(Status.CANCELLED_BINDER_DIED, /* shouldAbort= */ true, /* isVendorRequest= */ false); } @@ -219,18 +219,20 @@ final class VendorVibrationSession extends IVibrationSession.Stub @Override public void notifyVibratorCallback(int vibratorId, long vibrationId) { - // Ignore it, the session vibration playback doesn't depend on HAL timings + Slog.d(TAG, "Vibration callback received for vibration " + vibrationId + + " on vibrator " + vibratorId + ", ignoring..."); } @Override public void notifySyncedVibratorsCallback(long vibrationId) { - // Ignore it, the session vibration playback doesn't depend on HAL timings + Slog.d(TAG, "Synced vibration callback received for vibration " + vibrationId + + ", ignoring..."); } @Override public void notifySessionCallback() { + Slog.d(TAG, "Session callback received, ending vibration session..."); synchronized (mLock) { - Slog.d(TAG, "Session callback received, ending vibration session..."); // If end was not requested then the HAL has cancelled the session. maybeSetEndRequestLocked(Status.CANCELLED_BY_UNKNOWN_REASON, /* isVendorRequest= */ false); @@ -307,7 +309,7 @@ final class VendorVibrationSession extends IVibrationSession.Stub } } if (isAlreadyEnded) { - // Session already ended, make sure we end it in the HAL. + Slog.d(TAG, "Session already ended after starting the HAL, aborting..."); mHandler.post(() -> mManagerHooks.endSession(mSessionId, /* shouldAbort= */ true)); } } @@ -335,8 +337,8 @@ final class VendorVibrationSession extends IVibrationSession.Stub public boolean maybeSetVibrationConductor(VibrationStepConductor conductor) { synchronized (mLock) { if (mConductor != null) { - Slog.d(TAG, "Vibration session still dispatching previous vibration," - + " new vibration ignored"); + Slog.d(TAG, "Session still dispatching previous vibration, new vibration " + + conductor.getVibration().id + " ignored"); return false; } mConductor = conductor; @@ -345,19 +347,22 @@ final class VendorVibrationSession extends IVibrationSession.Stub } private void requestEndSession(Status status, boolean shouldAbort, boolean isVendorRequest) { + Slog.d(TAG, "Session end request received with status " + status); boolean shouldTriggerSessionHook = false; synchronized (mLock) { maybeSetEndRequestLocked(status, isVendorRequest); - if (isStarted()) { - // Always trigger session hook after it has started, in case new request aborts an - // already finishing session. Wait for HAL callback before actually ending here. + if (!isEnded() && isStarted()) { + // Trigger session hook even if it was already triggered, in case a second request + // is aborting the ongoing/ending session. This might cause it to end right away. + // Wait for HAL callback before setting the end status. shouldTriggerSessionHook = true; } else { - // Session did not start in the HAL, end it right away. + // Session not active in the HAL, set end status right away. maybeSetStatusToRequestedLocked(); } } if (shouldTriggerSessionHook) { + Slog.d(TAG, "Requesting HAL session end with abort=" + shouldAbort); mHandler.post(() -> mManagerHooks.endSession(mSessionId, shouldAbort)); } } @@ -368,6 +373,7 @@ final class VendorVibrationSession extends IVibrationSession.Stub // End already requested, keep first requested status and time. return; } + Slog.d(TAG, "Session end request accepted for status " + status); mEndStatusRequest = status; mEndedByVendor = isVendorRequest; mEndTime = System.currentTimeMillis(); @@ -400,6 +406,7 @@ final class VendorVibrationSession extends IVibrationSession.Stub // No end status was requested, nothing to set. return; } + Slog.d(TAG, "Session end request applied for status " + mEndStatusRequest); mStatus = mEndStatusRequest; // Run client callback in separate thread. final Status endStatus = mStatus; @@ -407,7 +414,7 @@ final class VendorVibrationSession extends IVibrationSession.Stub try { mCallback.onFinished(toSessionStatus(endStatus)); } catch (RemoteException e) { - Slog.e(TAG, "Error notifying vendor session is finishing", e); + Slog.e(TAG, "Error notifying vendor session finished", e); } }); } diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java index 9de46c878194..75b1b202bcfd 100644 --- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java +++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java @@ -61,6 +61,7 @@ import android.os.VibratorInfo; import android.os.vibrator.Flags; import android.os.vibrator.IVibrationSessionCallback; import android.os.vibrator.PrebakedSegment; +import android.os.vibrator.PrimitiveSegment; import android.os.vibrator.VibrationConfig; import android.os.vibrator.VibrationEffectSegment; import android.os.vibrator.VibratorInfoFactory; @@ -786,7 +787,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { synchronized (mLock) { if (DEBUG) { - Slog.d(TAG, "Starting session " + session.getSessionId()); + Slog.d(TAG, "Starting vendor session " + session.getSessionId()); } Status ignoreStatus = null; @@ -864,13 +865,16 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { // Session already ended, possibly cancelled by app cancellation signal. return session.getStatus(); } - int mode = startAppOpModeLocked(session.getCallerInfo()); + CallerInfo callerInfo = session.getCallerInfo(); + int mode = startAppOpModeLocked(callerInfo); switch (mode) { case AppOpsManager.MODE_ALLOWED: Trace.asyncTraceBegin(TRACE_TAG_VIBRATOR, "vibration", 0); // Make sure mCurrentVibration is set while triggering the HAL. mCurrentSession = session; if (!session.linkToDeath()) { + // Shouldn't happen. The method call already logs. + finishAppOpModeLocked(callerInfo); mCurrentSession = null; return Status.IGNORED_ERROR_TOKEN; } @@ -878,14 +882,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { Slog.e(TAG, "Error starting session " + sessionId + " on vibrators " + Arrays.toString(session.getVibratorIds())); session.unlinkToDeath(); + finishAppOpModeLocked(callerInfo); mCurrentSession = null; return Status.IGNORED_UNSUPPORTED; } session.notifyStart(); return null; case AppOpsManager.MODE_ERRORED: - Slog.w(TAG, "Start AppOpsManager operation errored for uid " - + session.getCallerInfo().uid); + Slog.w(TAG, "Start AppOpsManager operation errored for uid " + callerInfo.uid); return Status.IGNORED_ERROR_APP_OPS; default: return Status.IGNORED_APP_OPS; @@ -1081,11 +1085,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { @Nullable private Status startVibrationOnThreadLocked(SingleVibrationSession session) { if (DEBUG) { - Slog.d(TAG, "Starting vibration " + session.getVibration().id + " on thread"); + Slog.d(TAG, "Starting vibration " + session.getVibration().id + " on thread"); } VibrationStepConductor conductor = createVibrationStepConductor(session.getVibration()); session.setVibrationConductor(conductor); - int mode = startAppOpModeLocked(session.getCallerInfo()); + CallerInfo callerInfo = session.getCallerInfo(); + int mode = startAppOpModeLocked(callerInfo); switch (mode) { case AppOpsManager.MODE_ALLOWED: Trace.asyncTraceBegin(TRACE_TAG_VIBRATOR, "vibration", 0); @@ -1093,19 +1098,21 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { mCurrentSession = session; if (!mCurrentSession.linkToDeath()) { // Shouldn't happen. The method call already logs. + finishAppOpModeLocked(callerInfo); mCurrentSession = null; // Aborted. return Status.IGNORED_ERROR_TOKEN; } if (!mVibrationThread.runVibrationOnVibrationThread(conductor)) { // Shouldn't happen. The method call already logs. session.setVibrationConductor(null); // Rejected by thread, clear it in session. + mCurrentSession.unlinkToDeath(); + finishAppOpModeLocked(callerInfo); mCurrentSession = null; // Aborted. return Status.IGNORED_ERROR_SCHEDULING; } return null; case AppOpsManager.MODE_ERRORED: - Slog.w(TAG, "Start AppOpsManager operation errored for uid " - + session.getCallerInfo().uid); + Slog.w(TAG, "Start AppOpsManager operation errored for uid " + callerInfo.uid); return Status.IGNORED_ERROR_APP_OPS; default: return Status.IGNORED_APP_OPS; @@ -1494,6 +1501,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { private int checkAppOpModeLocked(CallerInfo callerInfo) { int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE, callerInfo.attrs.getAudioUsage(), callerInfo.uid, callerInfo.opPkg); + if (DEBUG) { + int opMode = mAppOps.checkOpNoThrow(AppOpsManager.OP_VIBRATE, callerInfo.uid, + callerInfo.opPkg); + Slog.d(TAG, "Check AppOp mode VIBRATE for uid " + callerInfo.uid + " and package " + + callerInfo.opPkg + " returned audio=" + AppOpsManager.MODE_NAMES[mode] + + ", op=" + AppOpsManager.MODE_NAMES[opMode]); + } int fixedMode = fixupAppOpModeLocked(mode, callerInfo.attrs); if (mode != fixedMode && fixedMode == AppOpsManager.MODE_ALLOWED) { // If we're just ignoring the vibration op then this is set by DND and we should ignore @@ -1507,9 +1521,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { /** Start an operation in {@link AppOpsManager}, if allowed. */ @GuardedBy("mLock") private int startAppOpModeLocked(CallerInfo callerInfo) { - return fixupAppOpModeLocked( - mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, callerInfo.uid, callerInfo.opPkg), - callerInfo.attrs); + int mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, callerInfo.uid, + callerInfo.opPkg); + if (DEBUG) { + Slog.d(TAG, "Start AppOp mode VIBRATE for uid " + callerInfo.uid + " and package " + + callerInfo.opPkg + " returned " + AppOpsManager.MODE_NAMES[mode]); + } + return fixupAppOpModeLocked(mode, callerInfo.attrs); } /** @@ -1518,6 +1536,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { */ @GuardedBy("mLock") private void finishAppOpModeLocked(CallerInfo callerInfo) { + if (DEBUG) { + Slog.d(TAG, "Finish AppOp mode VIBRATE for uid " + callerInfo.uid + " and package " + + callerInfo.opPkg); + } mAppOps.finishOp(AppOpsManager.OP_VIBRATE, callerInfo.uid, callerInfo.opPkg); } @@ -2651,7 +2673,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { CombinedVibration.ParallelCombination combination = CombinedVibration.startParallel(); while ("-v".equals(getNextOption())) { - int vibratorId = Integer.parseInt(getNextArgRequired()); + int vibratorId = parseInt(getNextArgRequired(), "Expected vibrator id after -v"); combination.addVibrator(vibratorId, nextEffect()); } runVibrate(commonOptions, combination.combine()); @@ -2663,7 +2685,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { CombinedVibration.SequentialCombination combination = CombinedVibration.startSequential(); while ("-v".equals(getNextOption())) { - int vibratorId = Integer.parseInt(getNextArgRequired()); + int vibratorId = parseInt(getNextArgRequired(), "Expected vibrator id after -v"); combination.addNext(vibratorId, nextEffect()); } runVibrate(commonOptions, combination.combine()); @@ -2688,7 +2710,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { private int runHapticFeedback() { CommonOptions commonOptions = new CommonOptions(); - int constant = Integer.parseInt(getNextArgRequired()); + int constant = parseInt(getNextArgRequired(), "Expected haptic feedback constant id"); IBinder deathBinder = commonOptions.background ? VibratorManagerService.this : mShellCallbacksToken; @@ -2736,12 +2758,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { if ("-a".equals(nextOption)) { hasAmplitude = true; } else if ("-w".equals(nextOption)) { - delay = Integer.parseInt(getNextArgRequired()); + delay = parseInt(getNextArgRequired(), "Expected delay millis after -w"); } } - long duration = Long.parseLong(getNextArgRequired()); - int amplitude = hasAmplitude ? Integer.parseInt(getNextArgRequired()) + long duration = parseInt(getNextArgRequired(), "Expected one-shot duration millis"); + int amplitude = hasAmplitude + ? parseInt(getNextArgRequired(), "Expected one-shot amplitude") : VibrationEffect.DEFAULT_AMPLITUDE; composition.addOffDuration(Duration.ofMillis(delay)); composition.addEffect(VibrationEffect.createOneShot(duration, amplitude)); @@ -2816,8 +2839,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { while ((nextOption = getNextOption()) != null) { switch (nextOption) { case "-a" -> isAdvanced = true; - case "-i" -> initialSharpness = Float.parseFloat(getNextArgRequired()); - case "-r" -> repeat = Integer.parseInt(getNextArgRequired()); + case "-i" -> initialSharpness = parseFloat(getNextArgRequired(), + "Expected initial sharpness after -i"); + case "-r" -> repeat = parseInt(getNextArgRequired(), + "Expected repeat index after -r"); } } @@ -2843,8 +2868,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { // nextArg is not a duration, finish reading. break; } - intensity = Float.parseFloat(getNextArgRequired()); - sharpness = Float.parseFloat(getNextArgRequired()); + intensity = parseFloat(getNextArgRequired(), "Expected envelope intensity"); + sharpness = parseFloat(getNextArgRequired(), "Expected envelope sharpness"); builder.addControlPoint(intensity, sharpness, duration); pos++; } @@ -2872,16 +2897,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { getNextArgRequired(); // consume "waveform" String nextOption; while ((nextOption = getNextOption()) != null) { - if ("-a".equals(nextOption)) { - hasAmplitudes = true; - } else if ("-r".equals(nextOption)) { - repeat = Integer.parseInt(getNextArgRequired()); - } else if ("-w".equals(nextOption)) { - delay = Integer.parseInt(getNextArgRequired()); - } else if ("-f".equals(nextOption)) { - hasFrequencies = true; - } else if ("-c".equals(nextOption)) { - isContinuous = true; + switch (nextOption) { + case "-a" -> hasAmplitudes = true; + case "-f" -> hasFrequencies = true; + case "-c" -> isContinuous = true; + case "-r" -> repeat = parseInt(getNextArgRequired(), + "Expected repeat index after -r"); + case "-w" -> delay = parseInt(getNextArgRequired(), + "Expected delay millis after -w"); } } List<Integer> durations = new ArrayList<>(); @@ -2899,14 +2922,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { break; } if (hasAmplitudes) { - amplitudes.add( - Float.parseFloat(getNextArgRequired()) / VibrationEffect.MAX_AMPLITUDE); + int amplitude = parseInt(getNextArgRequired(), "Expected waveform amplitude"); + amplitudes.add((float) amplitude / VibrationEffect.MAX_AMPLITUDE); } else { amplitudes.add(nextAmplitude); nextAmplitude = 1 - nextAmplitude; } if (hasFrequencies) { - frequencies.add(Float.parseFloat(getNextArgRequired())); + frequencies.add( + parseFloat(getNextArgRequired(), "Expected waveform frequency")); } } @@ -2965,27 +2989,37 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { if ("-b".equals(nextOption)) { shouldFallback = true; } else if ("-w".equals(nextOption)) { - delay = Integer.parseInt(getNextArgRequired()); + delay = parseInt(getNextArgRequired(), "Expected delay millis after -w"); } } - int effectId = Integer.parseInt(getNextArgRequired()); + int effectId = parseInt(getNextArgRequired(), "Expected prebaked effect id"); composition.addOffDuration(Duration.ofMillis(delay)); composition.addEffect(VibrationEffect.get(effectId, shouldFallback)); } private void addPrimitivesToComposition(VibrationEffect.Composition composition) { getNextArgRequired(); // consume "primitives" - String nextArg; - while ((nextArg = peekNextArg()) != null) { + while (peekNextArg() != null) { int delay = 0; - if ("-w".equals(nextArg)) { - getNextArgRequired(); // consume "-w" - delay = Integer.parseInt(getNextArgRequired()); - nextArg = peekNextArg(); + float scale = 1f; + int delayType = PrimitiveSegment.DEFAULT_DELAY_TYPE; + + String nextOption; + while ((nextOption = getNextOption()) != null) { + if ("-s".equals(nextOption)) { + scale = parseFloat(getNextArgRequired(), "Expected scale after -s"); + } else if ("-o".equals(nextOption)) { + delayType = VibrationEffect.Composition.DELAY_TYPE_RELATIVE_START_OFFSET; + delay = parseInt(getNextArgRequired(), "Expected offset millis after -o"); + } else if ("-w".equals(nextOption)) { + delayType = PrimitiveSegment.DEFAULT_DELAY_TYPE; + delay = parseInt(getNextArgRequired(), "Expected delay millis after -w"); + } } try { - composition.addPrimitive(Integer.parseInt(nextArg), /* scale= */ 1, delay); + String nextArg = peekNextArg(); // Just in case this is not a primitive. + composition.addPrimitive(Integer.parseInt(nextArg), scale, delay, delayType); getNextArgRequired(); // consume the primitive id } catch (NumberFormatException | NullPointerException e) { // nextArg is not describing a primitive, leave it to be consumed by outer loops @@ -3011,17 +3045,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { VibrationXmlParser.parseDocument(new StringReader(xml)); VibratorInfo combinedVibratorInfo = getCombinedVibratorInfo(); if (combinedVibratorInfo == null) { - throw new IllegalStateException( - "No combined vibrator info to parse vibration XML " + xml); + throw new IllegalStateException("No vibrator info available to parse XML"); } VibrationEffect effect = parsedVibration.resolve(combinedVibratorInfo); if (effect == null) { - throw new IllegalArgumentException( - "Parsed vibration cannot be resolved for vibration XML " + xml); + throw new IllegalArgumentException("Parsed XML cannot be resolved: " + xml); } return CombinedVibration.createParallel(effect); } catch (IOException e) { - throw new RuntimeException("Error parsing vibration XML " + xml, e); + throw new RuntimeException("Error parsing XML: " + xml, e); } } @@ -3039,16 +3071,30 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } } + private static int parseInt(String text, String errorMessage) { + try { + return Integer.parseInt(text); + } catch (NumberFormatException | NullPointerException e) { + throw new IllegalArgumentException(errorMessage, e); + } + } + + private static float parseFloat(String text, String errorMessage) { + try { + return Float.parseFloat(text); + } catch (NumberFormatException | NullPointerException e) { + throw new IllegalArgumentException(errorMessage, e); + } + } + @Override public void onHelp() { try (PrintWriter pw = getOutPrintWriter();) { pw.println("Vibrator Manager commands:"); pw.println(" help"); pw.println(" Prints this help text."); - pw.println(""); pw.println(" list"); - pw.println(" Prints the id of device vibrators. This does not include any "); - pw.println(" connected input device."); + pw.println(" Prints device vibrator ids; does not include input devices."); pw.println(" synced [options] <effect>..."); pw.println(" Vibrates effect on all vibrators in sync."); pw.println(" combined [options] (-v <vibrator-id> <effect>...)..."); @@ -3058,51 +3104,41 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { pw.println(" xml [options] <xml>"); pw.println(" Vibrates using combined vibration described in given XML string"); pw.println(" on all vibrators in sync. The XML could be:"); - pw.println(" XML containing a single effect, or"); - pw.println(" A vibration select XML containing multiple effects."); - pw.println(" Vibrates using combined vibration described in given XML string."); - pw.println(" XML containing a single effect it runs on all vibrators in sync."); + pw.println(" A single <vibration-effect>, or"); + pw.println(" A <vibration-select> containing multiple effects."); + pw.println(" feedback [options] <constant>"); + pw.println(" Performs a haptic feedback with the given constant."); pw.println(" cancel"); pw.println(" Cancels any active vibration"); - pw.println(" feedback [-f] [-d <description>] <constant>"); - pw.println(" Performs a haptic feedback with the given constant."); - pw.println(" The force (-f) option enables the `always` configuration, which"); - pw.println(" plays the haptic irrespective of the vibration intensity settings"); pw.println(""); pw.println("Effect commands:"); pw.println(" oneshot [-w delay] [-a] <duration> [<amplitude>]"); - pw.println(" Vibrates for duration milliseconds; ignored when device is on "); - pw.println(" DND (Do Not Disturb) mode; touch feedback strength user setting "); - pw.println(" will be used to scale amplitude."); + pw.println(" Vibrates for duration milliseconds."); pw.println(" If -w is provided, the effect will be played after the specified"); pw.println(" wait time in milliseconds."); pw.println(" If -a is provided, the command accepts a second argument for "); pw.println(" amplitude, in a scale of 1-255."); pw.print(" waveform [-w delay] [-r index] [-a] [-f] [-c] "); pw.println("(<duration> [<amplitude>] [<frequency>])..."); - pw.println(" Vibrates for durations and amplitudes in list; ignored when "); - pw.println(" device is on DND (Do Not Disturb) mode; touch feedback strength "); - pw.println(" user setting will be used to scale amplitude."); + pw.println(" Vibrates for durations and amplitudes in list."); pw.println(" If -w is provided, the effect will be played after the specified"); pw.println(" wait time in milliseconds."); pw.println(" If -r is provided, the waveform loops back to the specified"); - pw.println(" index (e.g. 0 loops from the beginning)"); + pw.println(" index (e.g. 0 loops from the beginning)."); pw.println(" If -a is provided, the command expects amplitude to follow each"); pw.println(" duration; otherwise, it accepts durations only and alternates"); - pw.println(" off/on"); + pw.println(" off/on."); pw.println(" If -f is provided, the command expects frequency to follow each"); - pw.println(" amplitude or duration; otherwise, it uses resonant frequency"); + pw.println(" amplitude or duration; otherwise, it uses resonant frequency."); pw.println(" If -c is provided, the waveform is continuous and will ramp"); pw.println(" between values; otherwise each entry is a fixed step."); pw.println(" Duration is in milliseconds; amplitude is a scale of 1-255;"); - pw.println(" frequency is an absolute value in hertz;"); + pw.println(" frequency is an absolute value in hertz."); pw.print(" envelope [-a] [-i initial sharpness] [-r index] "); pw.println("[<duration1> <intensity1> <sharpness1>]..."); pw.println(" Generates a vibration pattern based on a series of duration, "); pw.println(" intensity, and sharpness values. The total vibration time is "); - pw.println(" the sum of all durations; Ignored when device is on "); - pw.println(" DND (Do Not Disturb) mode; touch feedback strength user setting "); - pw.println(" will be used to scale amplitude."); + pw.println(" the sum of all durations."); pw.println(" If -a is provided, the waveform will use the advanced APIs to "); pw.println(" generate the vibration pattern and the input parameters "); pw.println(" become [<duration1> <amplitude1> <frequency1>]."); @@ -3111,19 +3147,20 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { pw.println(" If -r is provided, the waveform loops back to the specified index"); pw.println(" (e.g. 0 loops from the beginning)."); pw.println(" prebaked [-w delay] [-b] <effect-id>"); - pw.println(" Vibrates with prebaked effect; ignored when device is on DND "); - pw.println(" (Do Not Disturb) mode; touch feedback strength user setting "); - pw.println(" will be used to scale amplitude."); + pw.println(" Vibrates with prebaked effect."); pw.println(" If -w is provided, the effect will be played after the specified"); pw.println(" wait time in milliseconds."); pw.println(" If -b is provided, the prebaked fallback effect will be played if"); pw.println(" the device doesn't support the given effect-id."); - pw.println(" primitives ([-w delay] <primitive-id>)..."); - pw.println(" Vibrates with a composed effect; ignored when device is on DND "); - pw.println(" (Do Not Disturb) mode; touch feedback strength user setting "); - pw.println(" will be used to scale primitive intensities."); + pw.print(" primitives ([-w delay] [-o time] [-s scale]"); + pw.println("<primitive-id> [<scale>])..."); + pw.println(" Vibrates with a composed effect."); pw.println(" If -w is provided, the next primitive will be played after the "); pw.println(" specified wait time in milliseconds."); + pw.println(" If -o is provided, the next primitive will be played at the "); + pw.println(" specified start offset time in milliseconds."); + pw.println(" If -s is provided, the next primitive will be played with the"); + pw.println(" specified amplitude scale, in a scale of [0,1]."); pw.println(""); pw.println("Common Options:"); pw.println(" -f"); @@ -3134,6 +3171,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { pw.println(" -d <description>"); pw.println(" Add description to the vibration."); pw.println(""); + pw.println("Notes"); + pw.println(" Vibrations triggered by these commands will be ignored when"); + pw.println(" device is on DND (Do Not Disturb) mode; notification strength"); + pw.println(" user settings will be applied for scale."); + pw.println(""); } } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index 0a57cb50d681..c243cdc23137 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -617,6 +617,9 @@ public abstract class ActivityTaskManagerInternal { */ public abstract boolean isBaseOfLockedTask(String packageName); + /** Returns the value of {@link android.R.attr#windowNoDisplay} from the given theme. */ + public abstract boolean isNoDisplay(String packageName, int theme, int userId); + /** * Creates an interface to update configuration for the calling application. */ diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 4e2fade599ba..37783781a901 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -7448,6 +7448,18 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override + public boolean isNoDisplay(String packageName, int theme, int userId) { + if (!com.android.window.flags.Flags.cacheWindowStyle()) { + final AttributeCache.Entry ent = AttributeCache.instance() + .get(packageName, theme, R.styleable.Window, userId); + return ent != null + && ent.array.getBoolean(R.styleable.Window_windowNoDisplay, false); + } + final ActivityRecord.WindowStyle style = getWindowStyle(packageName, theme, userId); + return style != null && style.noDisplay(); + } + + @Override public PackageConfigurationUpdater createPackageConfigurationUpdater() { return new PackageConfigurationUpdaterImpl(Binder.getCallingPid(), ActivityTaskManagerService.this); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index fd322a5c6345..e30c24d87d20 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -162,6 +162,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerInternal; +import android.content.ComponentCallbacks; import android.content.ComponentName; import android.content.Context; import android.content.pm.ActivityInfo; @@ -456,6 +457,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp private DisplayInfo mLastDisplayInfoOverride; private final DisplayMetrics mDisplayMetrics = new DisplayMetrics(); + @NonNull private final DisplayPolicy mDisplayPolicy; private final DisplayRotation mDisplayRotation; @@ -542,6 +544,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp /** Remove this display when animation on it has completed. */ private boolean mDeferredRemoval; + @NonNull final PinnedTaskController mPinnedTaskController; private final LinkedList<ActivityRecord> mTmpUpdateAllDrawn = new LinkedList(); @@ -1102,6 +1105,29 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp }; /** + * Called to update fields retrieve from {@link #getDisplayUiContext()} resources when + * there's a configuration update on {@link #getDisplayUiContext()}. + */ + @NonNull + private final ComponentCallbacks mSysUiContextConfigCallback = new ComponentCallbacks() { + + @Override + public void onConfigurationChanged(@NonNull Configuration newConfig) { + synchronized (mWmService.mGlobalLock) { + if (mDisplayReady) { + mDisplayPolicy.onConfigurationChanged(); + mMinSizeOfResizeableTaskDp = getMinimalTaskSizeDp(); + } + } + } + + @Override + public void onLowMemory() { + // Do nothing. + } + }; + + /** * Create new {@link DisplayContent} instance, add itself to the root window container and * initialize direct children. * @param display May not be null. @@ -2797,11 +2823,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp final int lastOrientation = getConfiguration().orientation; final int lastWindowingMode = getWindowingMode(); super.onConfigurationChanged(newParentConfig); - if (mDisplayPolicy != null) { - mDisplayPolicy.onConfigurationChanged(); - mPinnedTaskController.onPostDisplayConfigurationChanged(); - mMinSizeOfResizeableTaskDp = getMinimalTaskSizeDp(); + if (!Flags.trackSystemUiContextBeforeWms()) { + mSysUiContextConfigCallback.onConfigurationChanged(newParentConfig); } + mPinnedTaskController.onPostDisplayConfigurationChanged(); // Update IME parent if needed. updateImeParent(); @@ -3381,6 +3406,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp .getKeyguardController().onDisplayRemoved(mDisplayId); mWallpaperController.resetLargestDisplay(mDisplay); mWmService.mDisplayWindowSettings.onDisplayRemoved(this); + if (Flags.trackSystemUiContextBeforeWms()) { + getDisplayUiContext().unregisterComponentCallbacks(mSysUiContextConfigCallback); + } } finally { mDisplayReady = false; } @@ -3817,7 +3845,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (mTmpWindow == null) { ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "findFocusedWindow: No focusable windows, display=%d", getDisplayId()); - return null; } return mTmpWindow; } @@ -5429,7 +5456,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp reconfigureDisplayLocked(); onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration()); mWmService.mDisplayNotificationController.dispatchDisplayAdded(this); - // Attach the SystemUiContext to this DisplayContent the get latest configuration. + // Attach the SystemUiContext to this DisplayContent to get latest configuration. // Note that the SystemUiContext will be removed automatically if this DisplayContent // is detached. registerSystemUiContext(); @@ -5437,11 +5464,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } private void registerSystemUiContext() { + final Context systemUiContext = getDisplayUiContext(); final WindowProcessController wpc = mAtmService.getProcessController( - getDisplayUiContext().getIApplicationThread()); + systemUiContext.getIApplicationThread()); mWmService.mWindowContextListenerController.registerWindowContainerListener( - wpc, getDisplayUiContext().getWindowContextToken(), this, + wpc, systemUiContext.getWindowContextToken(), this, INVALID_WINDOW_TYPE, null /* options */); + if (Flags.trackSystemUiContextBeforeWms()) { + systemUiContext.registerComponentCallbacks(mSysUiContextConfigCallback); + } } @Override @@ -6620,6 +6651,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp forAllTasks((t) -> { t.getRootTask().removeChild(t, "removeAllTasks"); }); } + @NonNull Context getDisplayUiContext() { return mDisplayPolicy.getSystemUiContext(); } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 10f591cfd379..fbe850198c50 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -1865,6 +1865,7 @@ public class DisplayPolicy { return mContext; } + @NonNull Context getSystemUiContext() { return mUiContext; } diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index a698a9e82929..bbda849262b2 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -1910,7 +1910,8 @@ class TaskFragment extends WindowContainer<WindowContainer> { if (!hasDirectChildActivities()) { return false; } - if (mResumedActivity != null && mTransitionController.isTransientLaunch(mResumedActivity)) { + if (mResumedActivity != null && !mResumedActivity.finishing + && mTransitionController.isTransientLaunch(mResumedActivity)) { // Even if the transient activity is occluded, defer pausing (addToStopping will still // be called) it until the transient transition is done. So the current resuming // activity won't need to wait for additional pause complete. diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index d699a689459e..0b20911bcab2 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1820,125 +1820,138 @@ public class WindowManagerService extends IWindowManager.Stub final boolean hideSystemAlertWindows = shouldHideNonSystemOverlayWindow(win); win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows); - boolean imMayMove = true; - - win.mToken.addWindow(win); - displayPolicy.addWindowLw(win, attrs); - displayPolicy.setDropInputModePolicy(win, win.mAttrs); - if (type == TYPE_APPLICATION_STARTING && activity != null) { - activity.attachStartingWindow(win); - ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s", - activity, win); - } else if (type == TYPE_INPUT_METHOD - // IME window is always touchable. - // Ignore non-touchable windows e.g. Stylus InkWindow.java. - && (win.getAttrs().flags & FLAG_NOT_TOUCHABLE) == 0) { - displayContent.setInputMethodWindowLocked(win); - imMayMove = false; - } else if (type == TYPE_INPUT_METHOD_DIALOG) { - displayContent.computeImeTarget(true /* updateImeTarget */); - imMayMove = false; - } else { - if (type == TYPE_WALLPAPER) { - displayContent.mWallpaperController.clearLastWallpaperTimeoutTime(); - displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; - } else if (win.hasWallpaper()) { - displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; - } else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) { - // If there is currently a wallpaper being shown, and - // the base layer of the new window is below the current - // layer of the target window, then adjust the wallpaper. - // This is to avoid a new window being placed between the - // wallpaper and its target. - displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; - } - } + res |= addWindowInner(win, displayPolicy, activity, displayContent, outInsetsState, + outAttachedFrame, outActiveControls, client, outSizeCompatScale, attrs); + } - final WindowStateAnimator winAnimator = win.mWinAnimator; - winAnimator.mEnterAnimationPending = true; - winAnimator.mEnteringAnimation = true; + Binder.restoreCallingIdentity(origId); - if (displayPolicy.areSystemBarsForcedConsumedLw()) { - res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS; - } - if (displayContent.isInTouchMode()) { - res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE; - } - if (win.mActivityRecord == null || win.mActivityRecord.isClientVisible()) { - res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE; + return res; + } + + private int addWindowInner(@NonNull WindowState win, @NonNull DisplayPolicy displayPolicy, + @NonNull ActivityRecord activity, @NonNull DisplayContent displayContent, + @NonNull InsetsState outInsetsState, @NonNull Rect outAttachedFrame, + @NonNull InsetsSourceControl.Array outActiveControls, @NonNull IWindow client, + @NonNull float[] outSizeCompatScale, @NonNull LayoutParams attrs) { + int res = 0; + final int type = attrs.type; + boolean imMayMove = true; + + win.mToken.addWindow(win); + displayPolicy.addWindowLw(win, attrs); + displayPolicy.setDropInputModePolicy(win, win.mAttrs); + if (type == TYPE_APPLICATION_STARTING && activity != null) { + activity.attachStartingWindow(win); + ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s", + activity, win); + } else if (type == TYPE_INPUT_METHOD + // IME window is always touchable. + // Ignore non-touchable windows e.g. Stylus InkWindow.java. + && (win.getAttrs().flags & FLAG_NOT_TOUCHABLE) == 0) { + displayContent.setInputMethodWindowLocked(win); + imMayMove = false; + } else if (type == TYPE_INPUT_METHOD_DIALOG) { + displayContent.computeImeTarget(true /* updateImeTarget */); + imMayMove = false; + } else { + if (type == TYPE_WALLPAPER) { + displayContent.mWallpaperController.clearLastWallpaperTimeoutTime(); + displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; + } else if (win.hasWallpaper()) { + displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; + } else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) { + // If there is currently a wallpaper being shown, and + // the base layer of the new window is below the current + // layer of the target window, then adjust the wallpaper. + // This is to avoid a new window being placed between the + // wallpaper and its target. + displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; } + } - displayContent.getInputMonitor().setUpdateInputWindowsNeededLw(); + final WindowStateAnimator winAnimator = win.mWinAnimator; + winAnimator.mEnterAnimationPending = true; + winAnimator.mEnteringAnimation = true; - boolean focusChanged = false; - if (win.canReceiveKeys()) { - focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS, - false /*updateInputWindows*/); - if (focusChanged) { - imMayMove = false; - } - } + if (displayPolicy.areSystemBarsForcedConsumedLw()) { + res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS; + } + if (displayContent.isInTouchMode()) { + res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE; + } + if (win.mActivityRecord == null || win.mActivityRecord.isClientVisible()) { + res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE; + } - if (imMayMove) { - displayContent.computeImeTarget(true /* updateImeTarget */); - if (win.isImeOverlayLayeringTarget()) { - dispatchImeTargetOverlayVisibilityChanged(client.asBinder(), win.mAttrs.type, - win.isVisibleRequestedOrAdding(), false /* removed */, - displayContent.getDisplayId()); - } - } + displayContent.getInputMonitor().setUpdateInputWindowsNeededLw(); - // Don't do layout here, the window must call - // relayout to be displayed, so we'll do it there. - if (win.mActivityRecord != null && win.mActivityRecord.isEmbedded()) { - // Assign child layers from the parent Task if the Activity is embedded. - win.getTask().assignChildLayers(); - } else { - win.getParent().assignChildLayers(); + boolean focusChanged = false; + if (win.canReceiveKeys()) { + focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS, + false /*updateInputWindows*/); + if (focusChanged) { + imMayMove = false; } + } - if (focusChanged) { - displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus, - false /*updateInputWindows*/); + if (imMayMove) { + displayContent.computeImeTarget(true /* updateImeTarget */); + if (win.isImeOverlayLayeringTarget()) { + dispatchImeTargetOverlayVisibilityChanged(client.asBinder(), win.mAttrs.type, + win.isVisibleRequestedOrAdding(), false /* removed */, + displayContent.getDisplayId()); } - displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/); + } - ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addWindow: New client %s" - + ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5)); + // Don't do layout here, the window must call + // relayout to be displayed, so we'll do it there. + if (win.mActivityRecord != null && win.mActivityRecord.isEmbedded()) { + // Assign child layers from the parent Task if the Activity is embedded. + win.getTask().assignChildLayers(); + } else { + win.getParent().assignChildLayers(); + } - boolean needToSendNewConfiguration = - win.isVisibleRequestedOrAdding() && displayContent.updateOrientation(); - if (win.providesDisplayDecorInsets()) { - needToSendNewConfiguration |= displayPolicy.updateDecorInsetsInfo(); - } - if (needToSendNewConfiguration) { - displayContent.sendNewConfiguration(); - } + if (focusChanged) { + displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus, + false /*updateInputWindows*/); + } + displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/); - // This window doesn't have a frame yet. Don't let this window cause the insets change. - displayContent.getInsetsStateController().updateAboveInsetsState( - false /* notifyInsetsChanged */); + ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addWindow: New client %s" + + ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5)); - win.fillInsetsState(outInsetsState, true /* copySources */); - getInsetsSourceControls(win, outActiveControls); + boolean needToSendNewConfiguration = + win.isVisibleRequestedOrAdding() && displayContent.updateOrientation(); + if (win.providesDisplayDecorInsets()) { + needToSendNewConfiguration |= displayPolicy.updateDecorInsetsInfo(); + } + if (needToSendNewConfiguration) { + displayContent.sendNewConfiguration(); + } - if (win.mLayoutAttached) { - outAttachedFrame.set(win.getParentWindow().getFrame()); - if (win.mInvGlobalScale != 1f) { - outAttachedFrame.scale(win.mInvGlobalScale); - } - } else { - // Make this invalid which indicates a null attached frame. - outAttachedFrame.set(0, 0, -1, -1); - } - outSizeCompatScale[0] = win.getCompatScaleForClient(); + // This window doesn't have a frame yet. Don't let this window cause the insets change. + displayContent.getInsetsStateController().updateAboveInsetsState( + false /* notifyInsetsChanged */); + + win.fillInsetsState(outInsetsState, true /* copySources */); + getInsetsSourceControls(win, outActiveControls); - if (res >= ADD_OKAY && win.isPresentation()) { - mPresentationController.onPresentationAdded(win); + if (win.mLayoutAttached) { + outAttachedFrame.set(win.getParentWindow().getFrame()); + if (win.mInvGlobalScale != 1f) { + outAttachedFrame.scale(win.mInvGlobalScale); } + } else { + // Make this invalid which indicates a null attached frame. + outAttachedFrame.set(0, 0, -1, -1); } + outSizeCompatScale[0] = win.getCompatScaleForClient(); - Binder.restoreCallingIdentity(origId); + if (res >= ADD_OKAY && win.isPresentation()) { + mPresentationController.onPresentationAdded(win); + } return res; } diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java index 2d3f7231cc5c..ae5e85163e9a 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java @@ -866,6 +866,9 @@ public class InputMethodServiceTest { () -> mActivity.showImeWithWindowInsetsController(), EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown"); + eventually(() -> assertWithMessage("IME navigation bar is shown") + .that(mInputMethodService.isImeNavigationBarShownForTesting()).isTrue()); + final var backButton = getUiObject(By.res(INPUT_METHOD_NAV_BACK_ID)); verifyInputViewStatus( () -> { @@ -892,6 +895,9 @@ public class InputMethodServiceTest { () -> mActivity.showImeWithWindowInsetsController(), EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown"); + eventually(() -> assertWithMessage("IME navigation bar is shown") + .that(mInputMethodService.isImeNavigationBarShownForTesting()).isTrue()); + final var backButton = getUiObject(By.res(INPUT_METHOD_NAV_BACK_ID)); verifyInputViewStatus( () -> { @@ -927,6 +933,9 @@ public class InputMethodServiceTest { }, EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown"); + eventually(() -> assertWithMessage("IME navigation bar is shown") + .that(mInputMethodService.isImeNavigationBarShownForTesting()).isTrue()); + final var imeSwitcherButton = getUiObject(By.res(INPUT_METHOD_NAV_IME_SWITCHER_ID)); imeSwitcherButton.click(); mInstrumentation.waitForIdleSync(); @@ -965,6 +974,9 @@ public class InputMethodServiceTest { }, EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown"); + eventually(() -> assertWithMessage("IME navigation bar is shown") + .that(mInputMethodService.isImeNavigationBarShownForTesting()).isTrue()); + final var imeSwitcherButton = getUiObject(By.res(INPUT_METHOD_NAV_IME_SWITCHER_ID)); imeSwitcherButton.longClick(); mInstrumentation.waitForIdleSync(); diff --git a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java index 7d25acd7f5e7..a42116351c2d 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java @@ -152,7 +152,7 @@ public class AutomaticBrightnessControllerTest { } @Override - AutomaticBrightnessController.Clock createClock(boolean isEnabled) { + AutomaticBrightnessController.Clock createClock() { return new AutomaticBrightnessController.Clock() { @Override public long uptimeMillis() { @@ -618,39 +618,46 @@ public class AutomaticBrightnessControllerTest { long increment = 500; // set autobrightness to low // t = 0 - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); // t = 500 mClock.fastForward(increment); - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); // t = 1000 mClock.fastForward(increment); - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); assertEquals(0.0f, mController.getAmbientLux(), EPSILON); // t = 1500 mClock.fastForward(increment); - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); assertEquals(0.0f, mController.getAmbientLux(), EPSILON); // t = 2000 // ensure that our reading is at 0. mClock.fastForward(increment); - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); assertEquals(0.0f, mController.getAmbientLux(), EPSILON); // t = 2500 // first 10000 lux sensor event reading mClock.fastForward(increment); - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); assertTrue(mController.getAmbientLux() > 0.0f); assertTrue(mController.getAmbientLux() < 10000.0f); // t = 3000 // lux reading should still not yet be 10000. mClock.fastForward(increment); - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); assertTrue(mController.getAmbientLux() > 0.0f); assertTrue(mController.getAmbientLux() < 10000.0f); @@ -659,45 +666,53 @@ public class AutomaticBrightnessControllerTest { // lux has been high (10000) for 1000ms. // lux reading should be 10000 // short horizon (ambient lux) is high, long horizon is still not high - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); assertEquals(10000.0f, mController.getAmbientLux(), EPSILON); // t = 4000 // stay high mClock.fastForward(increment); - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); assertEquals(10000.0f, mController.getAmbientLux(), EPSILON); // t = 4500 Mockito.clearInvocations(mBrightnessMappingStrategy); mClock.fastForward(increment); // short horizon is high, long horizon is high too - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); verify(mBrightnessMappingStrategy, times(1)).getBrightness(10000, null, -1); assertEquals(10000.0f, mController.getAmbientLux(), EPSILON); // t = 5000 mClock.fastForward(increment); - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); assertTrue(mController.getAmbientLux() > 0.0f); assertTrue(mController.getAmbientLux() < 10000.0f); // t = 5500 mClock.fastForward(increment); - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); assertTrue(mController.getAmbientLux() > 0.0f); assertTrue(mController.getAmbientLux() < 10000.0f); // t = 6000 mClock.fastForward(increment); // ambient lux goes to 0 - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); assertEquals(0.0f, mController.getAmbientLux(), EPSILON); // only the values within the horizon should be kept assertArrayEquals(new float[] {10000, 10000, 0, 0, 0}, mController.getLastSensorValues(), EPSILON); - assertArrayEquals(new long[] {4000, 4500, 5000, 5500, 6000}, + assertArrayEquals(new long[]{4000 + ANDROID_SLEEP_TIME, 4500 + ANDROID_SLEEP_TIME, + 5000 + ANDROID_SLEEP_TIME, 5500 + ANDROID_SLEEP_TIME, + 6000 + ANDROID_SLEEP_TIME}, mController.getLastSensorTimestamps()); } @@ -793,7 +808,8 @@ public class AutomaticBrightnessControllerTest { for (int i = 0; i < 1000; i++) { lux += increment; mClock.fastForward(increment); - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); } int valuesCount = (int) Math.ceil((double) AMBIENT_LIGHT_HORIZON_LONG / increment + 1); @@ -807,17 +823,17 @@ public class AutomaticBrightnessControllerTest { long sensorTimestamp = mClock.now(); for (int i = valuesCount - 1; i >= 1; i--) { assertEquals(lux, sensorValues[i], EPSILON); - assertEquals(sensorTimestamp, sensorTimestamps[i]); + assertEquals(sensorTimestamp + ANDROID_SLEEP_TIME, sensorTimestamps[i]); lux -= increment; sensorTimestamp -= increment; } assertEquals(lux, sensorValues[0], EPSILON); - assertEquals(mClock.now() - AMBIENT_LIGHT_HORIZON_LONG, sensorTimestamps[0]); + assertEquals(mClock.now() - AMBIENT_LIGHT_HORIZON_LONG + ANDROID_SLEEP_TIME, + sensorTimestamps[0]); } @Test public void testAmbientLuxBuffers_prunedBeyondLongHorizonExceptLatestValue() throws Exception { - when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true); ArgumentCaptor<SensorEventListener> listenerCaptor = ArgumentCaptor.forClass(SensorEventListener.class); verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor), @@ -867,7 +883,8 @@ public class AutomaticBrightnessControllerTest { for (int i = 0; i < 20; i++) { lux += increment1; mClock.fastForward(increment1); - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); } int valuesCount = (int) Math.ceil((double) AMBIENT_LIGHT_HORIZON_LONG / increment1 + 1); @@ -877,7 +894,8 @@ public class AutomaticBrightnessControllerTest { for (int i = 0; i < initialCapacity - valuesCount; i++) { lux += increment2; mClock.fastForward(increment2); - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); } float[] sensorValues = mController.getLastSensorValues(); @@ -890,7 +908,7 @@ public class AutomaticBrightnessControllerTest { long sensorTimestamp = mClock.now(); for (int i = initialCapacity - 1; i >= 1; i--) { assertEquals(lux, sensorValues[i], EPSILON); - assertEquals(sensorTimestamp, sensorTimestamps[i]); + assertEquals(sensorTimestamp + ANDROID_SLEEP_TIME, sensorTimestamps[i]); if (i >= valuesCount) { lux -= increment2; @@ -901,7 +919,8 @@ public class AutomaticBrightnessControllerTest { } } assertEquals(lux, sensorValues[0], EPSILON); - assertEquals(mClock.now() - AMBIENT_LIGHT_HORIZON_LONG, sensorTimestamps[0]); + assertEquals(mClock.now() - AMBIENT_LIGHT_HORIZON_LONG + ANDROID_SLEEP_TIME, + sensorTimestamps[0]); } @Test @@ -951,25 +970,29 @@ public class AutomaticBrightnessControllerTest { // t = 0 // Initial lux - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); assertEquals(500, mController.getAmbientLux(), EPSILON); // t = 1000 // Lux isn't steady yet mClock.fastForward(1000); - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); assertEquals(500, mController.getAmbientLux(), EPSILON); // t = 1500 // Lux isn't steady yet mClock.fastForward(500); - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); assertEquals(500, mController.getAmbientLux(), EPSILON); // t = 2500 // Lux is steady now mClock.fastForward(1000); - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); assertEquals(1200, mController.getAmbientLux(), EPSILON); } @@ -992,25 +1015,29 @@ public class AutomaticBrightnessControllerTest { // t = 0 // Initial lux - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); assertEquals(1200, mController.getAmbientLux(), EPSILON); // t = 2000 // Lux isn't steady yet mClock.fastForward(2000); - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); assertEquals(1200, mController.getAmbientLux(), EPSILON); // t = 2500 // Lux isn't steady yet mClock.fastForward(500); - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); assertEquals(1200, mController.getAmbientLux(), EPSILON); // t = 4500 // Lux is steady now mClock.fastForward(2000); - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); assertEquals(500, mController.getAmbientLux(), EPSILON); } @@ -1031,19 +1058,22 @@ public class AutomaticBrightnessControllerTest { // t = 0 // Initial lux - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); assertEquals(500, mController.getAmbientLux(), EPSILON); // t = 500 // Lux isn't steady yet mClock.fastForward(500); - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); assertEquals(500, mController.getAmbientLux(), EPSILON); // t = 1500 // Lux is steady now mClock.fastForward(1000); - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); assertEquals(1200, mController.getAmbientLux(), EPSILON); } @@ -1068,19 +1098,22 @@ public class AutomaticBrightnessControllerTest { // t = 0 // Initial lux - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); assertEquals(1200, mController.getAmbientLux(), EPSILON); // t = 1000 // Lux isn't steady yet mClock.fastForward(1000); - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); assertEquals(1200, mController.getAmbientLux(), EPSILON); // t = 2500 // Lux is steady now mClock.fastForward(1500); - listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500, + (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER)); assertEquals(500, mController.getAmbientLux(), EPSILON); } diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java index aed1f9858660..db94958f769e 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -1066,7 +1066,6 @@ public final class DisplayPowerControllerTest { com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, true); mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true); - when(mDisplayManagerFlagsMock.offloadControlsDozeAutoBrightness()).thenReturn(true); when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(true); mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession); @@ -1172,7 +1171,6 @@ public final class DisplayPowerControllerTest { com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, true); mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true); - when(mDisplayManagerFlagsMock.offloadControlsDozeAutoBrightness()).thenReturn(true); when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(false); mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession); diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java index 2ebb6c2a3ce4..ef39167dbabc 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java @@ -240,7 +240,6 @@ public final class DisplayBrightnessStrategySelectorTest { @Test public void selectStrategyDoesNotSelectDozeStrategyWhenOffloadSessionAutoBrightnessIsEnabled() { - when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true); when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true); when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(true); when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn( @@ -378,7 +377,6 @@ public final class DisplayBrightnessStrategySelectorTest { @Test public void selectStrategy_selectsAutomaticStrategyWhenValid() { when(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()).thenReturn(true); - when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true); when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true); when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(true); when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn( @@ -409,7 +407,6 @@ public final class DisplayBrightnessStrategySelectorTest { @Test public void selectStrategy_doesNotSelectAutomaticStrategyWhenStylusInUse() { when(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()).thenReturn(true); - when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true); when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true); when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(true); when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn( @@ -536,7 +533,6 @@ public final class DisplayBrightnessStrategySelectorTest { @Test public void setAllowAutoBrightnessWhileDozing_enabledWhenConfigAndOffloadSessionAreEnabled() { - when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true); when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true); when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(true); when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn( @@ -550,7 +546,6 @@ public final class DisplayBrightnessStrategySelectorTest { @Test public void setAllowAutoBrightnessWhileDozing_disabledWhenOffloadSessionFlagIsDisabled() { - when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true); when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true); when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(false); when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn( @@ -564,7 +559,6 @@ public final class DisplayBrightnessStrategySelectorTest { @Test public void setAllowAutoBrightnessWhileDozing_disabledWhenABWhileDozingConfigIsDisabled() { - when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true); when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true); when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(true); when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn( @@ -588,7 +582,6 @@ public final class DisplayBrightnessStrategySelectorTest { @Test public void setAllowAutoBrightnessWhileDozing_EnabledWhenFlagsAreDisabled() { - when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true); when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn( true); mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext, @@ -600,11 +593,5 @@ public final class DisplayBrightnessStrategySelectorTest { mDisplayBrightnessStrategySelector .setAllowAutoBrightnessWhileDozing(mDisplayOffloadSession); assertTrue(mDisplayBrightnessStrategySelector.isAllowAutoBrightnessWhileDozing()); - - when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true); - when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(false); - mDisplayBrightnessStrategySelector - .setAllowAutoBrightnessWhileDozing(mDisplayOffloadSession); - assertTrue(mDisplayBrightnessStrategySelector.isAllowAutoBrightnessWhileDozing()); } } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java index fa5847560782..4b53f1309337 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java @@ -23,18 +23,11 @@ import static com.android.server.am.ActivityManagerService.Injector; import static com.google.common.truth.Truth.assertThat; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import android.app.ActivityManagerInternal; import android.app.ActivityManagerInternal.FrozenProcessListener; import android.content.ComponentName; import android.content.Context; @@ -44,14 +37,12 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.MessageQueue; import android.os.Process; -import android.os.SystemClock; import android.platform.test.annotations.Presubmit; import android.provider.DeviceConfig; import android.text.TextUtils; import androidx.test.platform.app.InstrumentationRegistry; -import com.android.internal.annotations.GuardedBy; import com.android.modules.utils.testing.ExtendedMockitoRule; import com.android.modules.utils.testing.TestableDeviceConfig; import com.android.server.LocalServices; @@ -68,11 +59,9 @@ import org.mockito.Mock; import java.io.File; import java.io.IOException; -import java.util.ArrayList; import java.util.HashSet; import java.util.Set; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; /** @@ -164,7 +153,7 @@ public final class CachedAppOptimizerTest { app.info.uid = packageUid; // Exact value does not mater, it can be any state for which compaction is allowed. app.mState.setSetProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE); - app.mState.setSetAdj(899); + app.mState.setSetAdj(940); app.mState.setCurAdj(940); return app; } diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobServiceContextTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobServiceContextTest.java index 904545bd3cc3..b4e845171a0b 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/JobServiceContextTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/JobServiceContextTest.java @@ -276,6 +276,7 @@ public class JobServiceContextTest { final int jobId = 123; mJobServiceContext.setRunningJobLockedForTest(mMockJobStatus); mJobServiceContext.setRunningCallbackLockedForTest(mMockJobCallback); + mJobServiceContext.mVerb = JobServiceContext.VERB_EXECUTING; doReturn(jobId).when(mMockJobStatus).getJobId(); mJobServiceContext.doHandleAbandonedJob(mMockJobCallback, jobId); @@ -296,7 +297,25 @@ public class JobServiceContextTest { mJobServiceContext.setRunningCallbackLockedForTest(mMockJobCallback); mJobServiceContext.doHandleAbandonedJob(mMockJobCallback, jobId); + verify(mMockJobStatus, never()).setAbandoned(true); + + mJobServiceContext.setRunningJobLockedForTest(mMockJobStatus); + doReturn(jobId).when(mMockJobStatus).getJobId(); + + mJobServiceContext.mVerb = JobServiceContext.VERB_BINDING; + mJobServiceContext.doHandleAbandonedJob(mMockJobCallback, jobId); + verify(mMockJobStatus, never()).setAbandoned(true); + mJobServiceContext.mVerb = JobServiceContext.VERB_STARTING; + mJobServiceContext.doHandleAbandonedJob(mMockJobCallback, jobId); + verify(mMockJobStatus, never()).setAbandoned(true); + + mJobServiceContext.mVerb = JobServiceContext.VERB_STOPPING; + mJobServiceContext.doHandleAbandonedJob(mMockJobCallback, jobId); + verify(mMockJobStatus, never()).setAbandoned(true); + + mJobServiceContext.mVerb = JobServiceContext.VERB_FINISHED; + mJobServiceContext.doHandleAbandonedJob(mMockJobCallback, jobId); verify(mMockJobStatus, never()).setAbandoned(true); } diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index c6870adb8464..4e4b3df3c935 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -295,15 +295,15 @@ public class QuotaControllerTest { } private void setCharging() { - doReturn(true).when(mJobSchedulerService).isBatteryCharging(); synchronized (mQuotaController.mLock) { + doReturn(true).when(mJobSchedulerService).isBatteryCharging(); mQuotaController.onBatteryStateChangedLocked(); } } private void setDischarging() { - doReturn(false).when(mJobSchedulerService).isBatteryCharging(); synchronized (mQuotaController.mLock) { + doReturn(false).when(mJobSchedulerService).isBatteryCharging(); mQuotaController.onBatteryStateChangedLocked(); } } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderPerfTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderPerfTest.java index 8fc8c9f677a6..6be9c6d4b80c 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderPerfTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderPerfTest.java @@ -48,6 +48,7 @@ import org.junit.runner.RunWith; import java.io.File; import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; @@ -121,7 +122,7 @@ public class BatteryUsageStatsProviderPerfTest { } @Test - public void getBatteryUsageStats_accumulated() { + public void getBatteryUsageStats_accumulated() throws IOException { BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder() .setMaxStatsAgeMs(0) .includePowerStateData() @@ -155,6 +156,8 @@ public class BatteryUsageStatsProviderPerfTest { // Verify that all iterations produce the same result assertThat(cpuConsumedPower).isEqualTo(expectedCpuPower); } + stats.close(); + state.resumeTiming(); } } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CpuPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CpuPowerStatsProcessorTest.java index cb9d9b12a2fc..dcddf06f01fb 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CpuPowerStatsProcessorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CpuPowerStatsProcessorTest.java @@ -34,6 +34,7 @@ import static org.junit.Assert.fail; import android.os.BatteryConsumer; import android.os.PersistableBundle; import android.platform.test.ravenwood.RavenwoodRule; +import android.util.IntArray; import android.util.LongArray; import androidx.test.filters.SmallTest; @@ -51,7 +52,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.Arrays; -import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -315,8 +315,12 @@ public class CpuPowerStatsProcessorTest { } @Override - void collectUids(Collection<Integer> uids) { - uids.addAll(mUids); + IntArray getActiveUids() { + IntArray uids = new IntArray(); + for (Integer uid : mUids) { + uids.add(uid); + } + return uids; } void verifyPowerEstimates() { diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStateStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStateStatsTest.java index a232c0c7aec9..3b614bdb38ed 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStateStatsTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStateStatsTest.java @@ -143,6 +143,8 @@ public class MultiStateStatsTest { multiStateStats.increment(new long[]{200, 200}, 5000); + multiStateStats.increment(null, 6000); // No-op + long[] stats = new long[DIMENSION_COUNT]; multiStateStats.getStats(stats, new int[]{0, BatteryConsumer.PROCESS_STATE_FOREGROUND, 0}); // (400 - 100) * 0.5 + (600 - 400) * 0.5 diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index d702cae248a9..009ce88cfd6c 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -33,6 +33,9 @@ android_test { "test-apps/DisplayManagerTestApp/src/**/*.java", ], + kotlincflags: [ + "-Werror", + ], static_libs: [ "a11ychecker", "aatf", diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index 834fea46e505..4531b3948495 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -199,6 +199,10 @@ <service android:name="com.android.server.job.MockBiasJobService" android:permission="android.permission.BIND_JOB_SERVICE"/> + <activity + android:name="android.app.Activity" + android:exported="false" /> + <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity"/> <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity2"/> <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity3"/> diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index e0023e59af50..30aa8cebdff6 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -1768,7 +1768,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test - @Ignore // b/396073342 public void testCertificateDisclosure() throws Exception { final int userId = CALLER_USER_HANDLE; final UserHandle user = UserHandle.of(userId); @@ -4613,7 +4612,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test - @Ignore // b/396073342 public void testGetLastBugReportRequestTime() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -4661,7 +4659,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test - @Ignore // b/396073342 public void testGetLastNetworkLogRetrievalTime() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -6444,7 +6441,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test - @Ignore // b/396073342 public void testGetOwnerInstalledCaCertsForDeviceOwner() throws Exception { mServiceContext.packageName = mRealTestContext.getPackageName(); mServiceContext.applicationInfo = new ApplicationInfo(); @@ -6456,7 +6452,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test - @Ignore // b/396073342 public void testGetOwnerInstalledCaCertsForProfileOwner() throws Exception { mServiceContext.packageName = mRealTestContext.getPackageName(); mServiceContext.applicationInfo = new ApplicationInfo(); @@ -6469,7 +6464,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test - @Ignore // b/396073342 public void testGetOwnerInstalledCaCertsForDelegate() throws Exception { mServiceContext.packageName = mRealTestContext.getPackageName(); mServiceContext.applicationInfo = new ApplicationInfo(); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java index 87c9db2fe565..acbce36c3d7f 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java @@ -354,14 +354,20 @@ public abstract class BaseLockSettingsServiceTests { @After public void tearDown_baseServices() throws Exception { - mStorage.closeDatabase(); + if (mStorage != null) { + mStorage.closeDatabase(); + } File db = InstrumentationRegistry.getContext().getDatabasePath("locksettings.db"); assertTrue(!db.exists() || db.delete()); - File storageDir = mStorage.mStorageDir; - assertTrue(FileUtils.deleteContents(storageDir)); + if (mStorage != null) { + File storageDir = mStorage.mStorageDir; + assertTrue(FileUtils.deleteContents(storageDir)); + } - mPasswordSlotManager.cleanup(); + if (mPasswordSlotManager != null) { + mPasswordSlotManager.cleanup(); + } } protected void flushHandlerTasks() { diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java index 02b86db6ab6f..387b89a41eba 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java @@ -124,7 +124,9 @@ public class LockSettingsStorageTests { @After public void tearDown() throws Exception { - mStorage.closeDatabase(); + if (mStorage != null) { + mStorage.closeDatabase(); + } } @Test diff --git a/services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTests.java index 2faf6a2b29d1..2c2b9374fdf9 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTests.java @@ -49,7 +49,9 @@ public class PasswordSlotManagerTests { @After public void tearDown() throws Exception { - mManager.cleanup(); + if (mManager != null) { + mManager.cleanup(); + } } @Test diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java index 1514de04fb08..5add74e5b69e 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java @@ -156,9 +156,12 @@ public class KeySyncTaskTest { @After public void tearDown() { - mRecoverableKeyStoreDb.close(); - mDatabaseFile.delete(); - + if (mRecoverableKeyStoreDb != null) { + mRecoverableKeyStoreDb.close(); + } + if (mDatabaseFile != null) { + mDatabaseFile.delete(); + } File file = new File(InstrumentationRegistry.getTargetContext().getFilesDir(), SNAPSHOT_TOP_LEVEL_DIRECTORY); FileUtils.deleteContentsAndDir(file); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java index c09e09c8404f..46eaba7dace6 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java @@ -117,8 +117,12 @@ public class PlatformKeyManagerTest { @After public void tearDown() { - mRecoverableKeyStoreDb.close(); - mDatabaseFile.delete(); + if (mRecoverableKeyStoreDb != null) { + mRecoverableKeyStoreDb.close(); + } + if (mDatabaseFile != null) { + mDatabaseFile.delete(); + } } @Test diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java index 64130266b2c4..e6a6e36e75d6 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java @@ -89,8 +89,12 @@ public class RecoverableKeyGeneratorTest { keyStore.load(/*param=*/ null); keyStore.deleteEntry(WRAPPING_KEY_ALIAS); - mRecoverableKeyStoreDb.close(); - mDatabaseFile.delete(); + if (mRecoverableKeyStoreDb != null) { + mRecoverableKeyStoreDb.close(); + } + if (mDatabaseFile != null) { + mDatabaseFile.delete(); + } } @Test diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java index 7641fb957cc8..878c838e734b 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java @@ -230,9 +230,15 @@ public class RecoverableKeyStoreManagerTest { @After public void tearDown() { - mRemoteLockscreenValidationSessionStorage.finishSession(mUserId); - mRecoverableKeyStoreDb.close(); - mDatabaseFile.delete(); + if (mRemoteLockscreenValidationSessionStorage != null) { + mRemoteLockscreenValidationSessionStorage.finishSession(mUserId); + } + if (mRecoverableKeyStoreDb != null) { + mRecoverableKeyStoreDb.close(); + } + if (mDatabaseFile != null) { + mDatabaseFile.delete(); + } } @Test diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java index bbd9223718ae..fb98fab52ca0 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java @@ -18,6 +18,8 @@ package com.android.server.locksettings.recoverablekeystore.storage; import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.StandardCharsets.UTF_8; + import android.content.ContentValues; import android.content.Context; import android.database.sqlite.SQLiteDatabase; @@ -36,8 +38,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import static java.nio.charset.StandardCharsets.UTF_8; - @SmallTest @RunWith(AndroidJUnit4.class) public class RecoverableKeyStoreDbHelperTest { @@ -110,7 +110,9 @@ public class RecoverableKeyStoreDbHelperTest { @After public void tearDown() throws Exception { - mDatabase.close(); + if (mDatabase != null) { + mDatabase.close(); + } } private void createV2Tables() throws Exception { diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java index 8bc14fc54ae1..a77d8bcd3875 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java @@ -72,8 +72,12 @@ public class RecoverableKeyStoreDbTest { @After public void tearDown() { - mRecoverableKeyStoreDb.close(); - mDatabaseFile.delete(); + if (mRecoverableKeyStoreDb != null) { + mRecoverableKeyStoreDb.close(); + } + if (mDatabaseFile != null) { + mDatabaseFile.delete(); + } } @Test diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayConstraintsTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayConstraintsTests.java index b2e296a36b93..2912a0762761 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayConstraintsTests.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayConstraintsTests.java @@ -19,6 +19,7 @@ package com.android.server.om; import static android.content.Context.DEVICE_ID_DEFAULT; import static android.content.om.OverlayConstraint.TYPE_DEVICE_ID; import static android.content.om.OverlayConstraint.TYPE_DISPLAY_ID; +import static android.util.TypedValue.TYPE_STRING; import static android.view.Display.DEFAULT_DISPLAY; import static androidx.test.core.app.ApplicationProvider.getApplicationContext; @@ -28,6 +29,9 @@ import static junit.framework.Assert.assertNotNull; import static org.testng.Assert.assertThrows; +import android.app.Activity; +import android.companion.virtual.VirtualDeviceManager; +import android.content.Context; import android.content.om.FabricatedOverlay; import android.content.om.OverlayConstraint; import android.content.om.OverlayIdentifier; @@ -35,12 +39,12 @@ import android.content.om.OverlayInfo; import android.content.om.OverlayManager; import android.content.om.OverlayManagerTransaction; import android.content.res.Flags; +import android.content.res.Resources; import android.os.UserHandle; import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; -import android.platform.test.flag.junit.CheckFlagsRule; -import android.platform.test.flag.junit.DeviceFlagsValueProvider; -import android.util.TypedValue; +import android.view.Display; +import android.virtualdevice.cts.common.VirtualDeviceRule; import junitparams.JUnitParamsRunner; import junitparams.Parameters; @@ -53,20 +57,28 @@ import org.junit.runner.RunWith; import java.util.Collections; import java.util.List; +import java.util.Objects; +import java.util.concurrent.TimeoutException; @RunWith(JUnitParamsRunner.class) public class OverlayConstraintsTests { + private static final String RESOURCE_NAME = "string/module_2_name"; + private static final String RESOURCE_DEFAULT_VALUE = "module_2_name"; + private static final String RESOURCE_OVERLAID_VALUE = "hello"; + private static final long TIMEOUT_MILLIS = 2000L; @Rule - public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + public final VirtualDeviceRule mVirtualDeviceRule = VirtualDeviceRule.createDefault(); private OverlayManager mOverlayManager; private UserHandle mUserHandle; private OverlayIdentifier mOverlayIdentifier = null; + private final String mPackageName = getApplicationContext().getPackageName(); @Before public void setUp() throws Exception { - mOverlayManager = getApplicationContext().getSystemService(OverlayManager.class); + final Context context = getApplicationContext(); + mOverlayManager = context.getSystemService(OverlayManager.class); mUserHandle = UserHandle.of(UserHandle.myUserId()); } @@ -79,6 +91,7 @@ public class OverlayConstraintsTests { .build(); mOverlayManager.commit(transaction); mOverlayIdentifier = null; + waitForResourceValue(RESOURCE_DEFAULT_VALUE, getApplicationContext()); } } @@ -161,13 +174,161 @@ public class OverlayConstraintsTests { List.of(new OverlayConstraint(TYPE_DISPLAY_ID, DEFAULT_DISPLAY)))); } + @Test + @RequiresFlagsEnabled(Flags.FLAG_RRO_CONSTRAINTS) + public void enableOverlayWithoutConstraints_appliesOverlayWithoutConstraints() + throws Exception { + enableOverlay(Collections.emptyList()); + + // Assert than the overlay is applied for both default device context and virtual + // device context. + final Context context = getApplicationContext(); + waitForResourceValue(RESOURCE_OVERLAID_VALUE, context); + VirtualDeviceManager.VirtualDevice virtualDevice = + mVirtualDeviceRule.createManagedVirtualDevice(); + final Context deviceContext = context.createDeviceContext(virtualDevice.getDeviceId()); + waitForResourceValue(RESOURCE_OVERLAID_VALUE, deviceContext); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_RRO_CONSTRAINTS) + public void enableOverlayWithConstraints_withTypeDeviceId_appliesOverlayWithConstraints() + throws Exception { + final int deviceId1 = mVirtualDeviceRule.createManagedVirtualDevice().getDeviceId(); + final int deviceId2 = mVirtualDeviceRule.createManagedVirtualDevice().getDeviceId(); + enableOverlay(List.of(new OverlayConstraint(TYPE_DEVICE_ID, deviceId1), + new OverlayConstraint(TYPE_DEVICE_ID, deviceId2))); + + // Assert than the overlay is not applied for contexts not associated with the above + // devices. + final Context context = getApplicationContext(); + ensureResourceValueStaysAt(RESOURCE_DEFAULT_VALUE, context); + final int deviceId3 = mVirtualDeviceRule.createManagedVirtualDevice().getDeviceId(); + ensureResourceValueStaysAt(RESOURCE_DEFAULT_VALUE, context.createDeviceContext(deviceId3)); + + // Assert than the overlay is applied for contexts associated with the above devices. + waitForResourceValue(RESOURCE_OVERLAID_VALUE, context.createDeviceContext(deviceId1)); + waitForResourceValue(RESOURCE_OVERLAID_VALUE, context.createDeviceContext(deviceId2)); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_RRO_CONSTRAINTS) + public void enableOverlayWithConstraints_withTypeDisplayId_appliesOverlayWithConstraints() + throws Exception { + final Display display1 = + mVirtualDeviceRule.createManagedUnownedVirtualDisplay().getDisplay(); + final Display display2 = + mVirtualDeviceRule.createManagedUnownedVirtualDisplay().getDisplay(); + enableOverlay(List.of(new OverlayConstraint(TYPE_DISPLAY_ID, display1.getDisplayId()), + new OverlayConstraint(TYPE_DISPLAY_ID, display2.getDisplayId()))); + + // Assert than the overlay is not applied for contexts not associated with the above + // displays. + final Context context = getApplicationContext(); + ensureResourceValueStaysAt(RESOURCE_DEFAULT_VALUE, context); + final Display display3 = + mVirtualDeviceRule.createManagedUnownedVirtualDisplay().getDisplay(); + ensureResourceValueStaysAt(RESOURCE_DEFAULT_VALUE, context.createDisplayContext(display3)); + + // Assert than the overlay is applied for contexts associated with the above displays. + waitForResourceValue(RESOURCE_OVERLAID_VALUE, context.createDisplayContext(display1)); + waitForResourceValue(RESOURCE_OVERLAID_VALUE, context.createDisplayContext(display2)); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_RRO_CONSTRAINTS) + public void enableOverlayWithConstraints_withTypesDisplayIdAndDeviceId_appliesOverlayWithConstraints() + throws Exception { + final Display display1 = + mVirtualDeviceRule.createManagedUnownedVirtualDisplay().getDisplay(); + final int deviceId1 = mVirtualDeviceRule.createManagedVirtualDevice().getDeviceId(); + enableOverlay(List.of(new OverlayConstraint(TYPE_DISPLAY_ID, display1.getDisplayId()), + new OverlayConstraint(TYPE_DEVICE_ID, deviceId1))); + + // Assert than the overlay is not applied for contexts not associated with the above + // display or device. + final Context context = getApplicationContext(); + ensureResourceValueStaysAt(RESOURCE_DEFAULT_VALUE, context); + final Display display2 = + mVirtualDeviceRule.createManagedUnownedVirtualDisplay().getDisplay(); + ensureResourceValueStaysAt(RESOURCE_DEFAULT_VALUE, context.createDisplayContext(display2)); + final int deviceId2 = mVirtualDeviceRule.createManagedVirtualDevice().getDeviceId(); + ensureResourceValueStaysAt(RESOURCE_DEFAULT_VALUE, context.createDeviceContext(deviceId2)); + + // Assert than the overlay is applied for contexts associated with the above display or + // device. + waitForResourceValue(RESOURCE_OVERLAID_VALUE, context.createDisplayContext(display1)); + waitForResourceValue(RESOURCE_OVERLAID_VALUE, context.createDeviceContext(deviceId1)); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_RRO_CONSTRAINTS) + public void enableOverlayWithConstraints_withTypeDisplayId_appliesForActivityOnDisplay() + throws Exception { + final Display display = + mVirtualDeviceRule.createManagedUnownedVirtualDisplay( + VirtualDeviceRule.createTrustedVirtualDisplayConfigBuilder()) + .getDisplay(); + final Activity activityOnDefaultDisplay = mVirtualDeviceRule.startActivityOnDisplaySync( + DEFAULT_DISPLAY, Activity.class); + final Activity activityOnVirtualDisplay = mVirtualDeviceRule.startActivityOnDisplaySync( + display.getDisplayId(), Activity.class); + + enableOverlay(List.of(new OverlayConstraint(TYPE_DISPLAY_ID, display.getDisplayId()))); + + // Assert than the overlay is not applied for any existing activity on the default display. + ensureResourceValueStaysAt(RESOURCE_DEFAULT_VALUE, activityOnDefaultDisplay); + // Assert than the overlay is applied for any existing activity on the virtual display. + waitForResourceValue(RESOURCE_OVERLAID_VALUE, activityOnVirtualDisplay); + + // Assert than the overlay is not applied for any new activity on the default display. + final Activity newActivityOnDefaultDisplay = mVirtualDeviceRule.startActivityOnDisplaySync( + DEFAULT_DISPLAY, Activity.class); + ensureResourceValueStaysAt(RESOURCE_DEFAULT_VALUE, newActivityOnDefaultDisplay); + // Assert than the overlay is applied for any new activity on the virtual display. + final Activity newActivityOnVirtualDisplay = mVirtualDeviceRule.startActivityOnDisplaySync( + display.getDisplayId(), Activity.class); + waitForResourceValue(RESOURCE_OVERLAID_VALUE, newActivityOnVirtualDisplay); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_RRO_CONSTRAINTS) + public void enableOverlayWithConstraints_withTypeDeviceId_appliesForActivityOnDevice() + throws Exception { + final VirtualDeviceManager.VirtualDevice device = + mVirtualDeviceRule.createManagedVirtualDevice(); + final Display display = + mVirtualDeviceRule.createManagedVirtualDisplay(device, + VirtualDeviceRule.createTrustedVirtualDisplayConfigBuilder()) + .getDisplay(); + final Activity activityOnDefaultDevice = mVirtualDeviceRule.startActivityOnDisplaySync( + DEFAULT_DISPLAY, Activity.class); + final Activity activityOnVirtualDevice = mVirtualDeviceRule.startActivityOnDisplaySync( + display.getDisplayId(), Activity.class); + + enableOverlay(List.of(new OverlayConstraint(TYPE_DEVICE_ID, device.getDeviceId()))); + + // Assert than the overlay is not applied for any existing activity on the default device. + ensureResourceValueStaysAt(RESOURCE_DEFAULT_VALUE, activityOnDefaultDevice); + // Assert than the overlay is applied for any existing activity on the virtual device. + waitForResourceValue(RESOURCE_OVERLAID_VALUE, activityOnVirtualDevice); + + // Assert than the overlay is not applied for any new activity on the default device. + final Activity newActivityOnDefaultDevice = mVirtualDeviceRule.startActivityOnDisplaySync( + DEFAULT_DISPLAY, Activity.class); + ensureResourceValueStaysAt(RESOURCE_DEFAULT_VALUE, newActivityOnDefaultDevice); + // Assert than the overlay is applied for any new activity on the virtual device. + final Activity newActivityOnVirtualDevice = mVirtualDeviceRule.startActivityOnDisplaySync( + display.getDisplayId(), Activity.class); + waitForResourceValue(RESOURCE_OVERLAID_VALUE, newActivityOnVirtualDevice); + } + private FabricatedOverlay createFabricatedOverlay() { - String packageName = getApplicationContext().getPackageName(); FabricatedOverlay fabricatedOverlay = new FabricatedOverlay.Builder( - packageName, "testOverlay" /* name */, packageName) + mPackageName, "testOverlay" /* name */, mPackageName) .build(); - fabricatedOverlay.setResourceValue("string/module_2_name" /* resourceName */, - TypedValue.TYPE_STRING, "hello" /* value */, null /* configuration */); + fabricatedOverlay.setResourceValue(RESOURCE_NAME, TYPE_STRING, RESOURCE_OVERLAID_VALUE, + null /* configuration */); return fabricatedOverlay; } @@ -183,6 +344,37 @@ public class OverlayConstraintsTests { mOverlayIdentifier = fabricatedOverlay.getIdentifier(); } + private static void waitForResourceValue(final String expectedValue, Context context) + throws TimeoutException { + final long endTime = System.currentTimeMillis() + TIMEOUT_MILLIS; + final Resources resources = context.getResources(); + final int resourceId = getResourceId(context); + String resourceValue = null; + while (System.currentTimeMillis() < endTime) { + resourceValue = resources.getString(resourceId); + if (Objects.equals(resourceValue, expectedValue)) { + return; + } + } + throw new TimeoutException("Timed out waiting for '" + RESOURCE_NAME + "' value to equal '" + + expectedValue + "': current value is '" + resourceValue + "'"); + } + + private static void ensureResourceValueStaysAt(final String expectedValue, Context context) { + final long endTime = System.currentTimeMillis() + TIMEOUT_MILLIS; + final Resources resources = context.getResources(); + final int resourceId = getResourceId(context); + String resourceValue; + while (System.currentTimeMillis() < endTime) { + resourceValue = resources.getString(resourceId); + assertEquals(expectedValue, resourceValue); + } + } + + private static int getResourceId(Context context) { + return context.getResources().getIdentifier(RESOURCE_NAME, "", context.getPackageName()); + } + private static List<OverlayConstraint>[] getAllConstraintLists() { return new List[]{ Collections.emptyList(), diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java index 8e2cea7a8c78..6d8a48799112 100644 --- a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java @@ -244,9 +244,6 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase { {"Meta + L -> Lock Homescreen", new int[]{META_KEY, KeyEvent.KEYCODE_L}, KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN, KeyEvent.KEYCODE_L, META_ON}, - {"Meta + Ctrl + N -> Open Notes", new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_N}, - KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES, KeyEvent.KEYCODE_N, - META_ON | CTRL_ON}, {"Meta + Ctrl + DPAD_DOWN -> Enter desktop mode", new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_DOWN}, KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE, diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java index 32a3b7f2c9cc..22c86eb3a9b8 100644 --- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java @@ -272,6 +272,19 @@ public class PhoneWindowManagerTests { } @Test + public void powerPress_withoutDreamManagerInternal_doesNotCrash() { + when(mDisplayPolicy.isAwake()).thenReturn(true); + mDreamManagerInternal = null; + initPhoneWindowManager(); + + // Power button pressed. + int eventTime = 0; + mPhoneWindowManager.powerPress(eventTime, 1, 0); + + // verify no crash + } + + @Test public void powerPress_hubOrDreamOrSleep_hubAvailableLocks() { when(mDisplayPolicy.isAwake()).thenReturn(true); mContext.getTestablePermissions().setPermission(android.Manifest.permission.DEVICE_POWER, @@ -352,5 +365,10 @@ public class PhoneWindowManagerTests { WindowWakeUpPolicy getWindowWakeUpPolicy() { return mock(WindowWakeUpPolicy.class); } + + @Override + DreamManagerInternal getDreamManagerInternal() { + return mDreamManagerInternal; + } } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 6923deec1ebb..d53ba1d24d8c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -1009,6 +1009,8 @@ public class ActivityRecordTests extends WindowTestsBase { assertTrue(style.disablePreview()); assertTrue(style.optOutEdgeToEdge()); assertEquals(1 /* icon_preferred */, style.mSplashScreenBehavior); + assertEquals(style.noDisplay(), mAtm.mInternal.isNoDisplay(activity.packageName, + activity.info.theme, activity.mUserId)); } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index 59335d3bb135..9406779c929d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -98,6 +98,7 @@ import com.android.server.policy.PermissionPolicyInternal; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.testutils.StubTransaction; import com.android.server.uri.UriGrantsManagerInternal; +import com.android.window.flags.Flags; import org.junit.rules.TestRule; import org.junit.runner.Description; @@ -657,6 +658,13 @@ public class SystemServicesTestRule implements TestRule { AppWarnings appWarnings = getAppWarningsLocked(); spyOn(appWarnings); doNothing().when(appWarnings).onStartActivity(any()); + + if (Flags.trackSystemUiContextBeforeWms()) { + final Context uiContext = getUiContext(); + spyOn(uiContext); + doNothing().when(uiContext).registerComponentCallbacks(any()); + doNothing().when(uiContext).unregisterComponentCallbacks(any()); + } } @Override diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java index ccce57a81e41..b1525cf00dc6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java @@ -30,6 +30,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.Context; import android.content.res.Configuration; import android.graphics.Insets; import android.graphics.Rect; @@ -39,6 +40,7 @@ import android.view.DisplayCutout; import android.view.DisplayInfo; import com.android.server.wm.DisplayWindowSettings.SettingsProvider.SettingsEntry; +import com.android.window.flags.Flags; class TestDisplayContent extends DisplayContent { @@ -79,6 +81,13 @@ class TestDisplayContent extends DisplayContent { WindowTestsBase.suppressInsetsAnimation(insetsPolicy.getPermanentControlTarget()); WindowTestsBase.suppressInsetsAnimation(insetsPolicy.getTransientControlTarget()); + if (Flags.trackSystemUiContextBeforeWms()) { + final Context uiContext = getDisplayUiContext(); + spyOn(uiContext); + doNothing().when(uiContext).registerComponentCallbacks(any()); + doNothing().when(uiContext).unregisterComponentCallbacks(any()); + } + // For devices that set the sysprop ro.bootanim.set_orientation_<display_id> // See DisplayRotation#readDefaultDisplayRotation for context. // Without that, meaning of height and width in context of the tests can be swapped if diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 7f9e591ca5e3..97c6ac6854c3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -861,11 +861,9 @@ public class WindowTestsBase extends SystemServiceTestsBase { /** Creates a {@link DisplayContent} and adds it to the system. */ private DisplayContent createNewDisplay(DisplayInfo info, @DisplayImePolicy int imePolicy, @Nullable SettingsEntry overrideSettings) { - final DisplayContent display = - new TestDisplayContent.Builder(mAtm, info) - .setOverrideSettings(overrideSettings) - .build(); - final DisplayContent dc = display.mDisplayContent; + final DisplayContent dc = new TestDisplayContent.Builder(mAtm, info) + .setOverrideSettings(overrideSettings) + .build(); // this display can show IME. dc.mWmService.mDisplayWindowSettings.setDisplayImePolicy(dc, imePolicy); return dc; diff --git a/startop/OWNERS b/startop/OWNERS index 11d5ad0f000a..3414a7469ac2 100644 --- a/startop/OWNERS +++ b/startop/OWNERS @@ -1,2 +1 @@ include platform/art:/OWNERS -keunyoung@google.com diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp index 1f0bd61b5c3f..168141bf6e7d 100644 --- a/tests/Input/Android.bp +++ b/tests/Input/Android.bp @@ -19,6 +19,9 @@ android_test { "src/**/*.kt", ], asset_dirs: ["assets"], + kotlincflags: [ + "-Werror", + ], platform_apis: true, certificate: "platform", static_libs: [ diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt index 99c5bad7b2b9..c666fb7e05f1 100644 --- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt @@ -371,18 +371,6 @@ class KeyGestureControllerTests { intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) ), TestData( - "META + CTRL + N -> Open Notes", - intArrayOf( - KeyEvent.KEYCODE_META_LEFT, - KeyEvent.KEYCODE_CTRL_LEFT, - KeyEvent.KEYCODE_N - ), - KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES, - intArrayOf(KeyEvent.KEYCODE_N), - KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ), - TestData( "META + S -> Take Screenshot", intArrayOf( KeyEvent.KEYCODE_META_LEFT, diff --git a/tests/SoundTriggerTestApp/OWNERS b/tests/SoundTriggerTestApp/OWNERS index a0fcfc52704d..1e41886fe716 100644 --- a/tests/SoundTriggerTestApp/OWNERS +++ b/tests/SoundTriggerTestApp/OWNERS @@ -1,2 +1 @@ include /media/java/android/media/soundtrigger/OWNERS -mdooley@google.com diff --git a/tests/Tracing/src/com/android/internal/protolog/ProtoLogCommandHandlerTest.java b/tests/Tracing/src/com/android/internal/protolog/ProtoLogCommandHandlerTest.java index be0c7daebb57..e62a89b17927 100644 --- a/tests/Tracing/src/com/android/internal/protolog/ProtoLogCommandHandlerTest.java +++ b/tests/Tracing/src/com/android/internal/protolog/ProtoLogCommandHandlerTest.java @@ -19,6 +19,7 @@ package com.android.internal.protolog; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.contains; import static org.mockito.ArgumentMatchers.endsWith; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.times; @@ -157,13 +158,15 @@ public class ProtoLogCommandHandlerTest { cmdHandler.exec(mMockBinder, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err, new String[] { "logcat", "enable", "MY_GROUP" }); - Mockito.verify(mProtoLogConfigurationService).enableProtoLogToLogcat("MY_GROUP"); + Mockito.verify(mProtoLogConfigurationService) + .enableProtoLogToLogcat(Mockito.any(), eq("MY_GROUP")); cmdHandler.exec(mMockBinder, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err, new String[] { "logcat", "enable", "MY_GROUP", "MY_OTHER_GROUP" }); Mockito.verify(mProtoLogConfigurationService) - .enableProtoLogToLogcat("MY_GROUP", "MY_OTHER_GROUP"); + .enableProtoLogToLogcat(Mockito.any(), + eq("MY_GROUP"), eq("MY_OTHER_GROUP")); } @Test @@ -173,13 +176,15 @@ public class ProtoLogCommandHandlerTest { cmdHandler.exec(mMockBinder, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err, new String[] { "logcat", "disable", "MY_GROUP" }); - Mockito.verify(mProtoLogConfigurationService).disableProtoLogToLogcat("MY_GROUP"); + Mockito.verify(mProtoLogConfigurationService) + .disableProtoLogToLogcat(Mockito.any(), eq("MY_GROUP")); cmdHandler.exec(mMockBinder, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err, new String[] { "logcat", "disable", "MY_GROUP", "MY_OTHER_GROUP" }); Mockito.verify(mProtoLogConfigurationService) - .disableProtoLogToLogcat("MY_GROUP", "MY_OTHER_GROUP"); + .disableProtoLogToLogcat(Mockito.any(), + eq("MY_GROUP"), eq("MY_OTHER_GROUP")); } @Test diff --git a/tests/Tracing/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java b/tests/Tracing/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java index 3be725101252..1f3f91ebf557 100644 --- a/tests/Tracing/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java +++ b/tests/Tracing/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java @@ -62,6 +62,7 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.io.PrintWriter; import java.util.List; /** @@ -234,7 +235,7 @@ public class ProtoLogConfigurationServiceTest { service.registerClient(mMockClient, args); Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isFalse(); - service.enableProtoLogToLogcat(TEST_GROUP); + service.enableProtoLogToLogcat(Mockito.mock(PrintWriter.class), TEST_GROUP); Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue(); Mockito.verify(mMockClient).toggleLogcat(eq(true), @@ -251,7 +252,7 @@ public class ProtoLogConfigurationServiceTest { service.registerClient(mMockClient, args); Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue(); - service.disableProtoLogToLogcat(TEST_GROUP); + service.disableProtoLogToLogcat(Mockito.mock(PrintWriter.class), TEST_GROUP); Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isFalse(); Mockito.verify(mMockClient).toggleLogcat(eq(false), @@ -269,7 +270,7 @@ public class ProtoLogConfigurationServiceTest { service.registerClient(mMockClient, args); Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isFalse(); - service.enableProtoLogToLogcat(OTHER_TEST_GROUP); + service.enableProtoLogToLogcat(Mockito.mock(PrintWriter.class), OTHER_TEST_GROUP); Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isFalse(); Mockito.verify(mMockClient, never()).toggleLogcat(anyBoolean(), any()); @@ -280,7 +281,7 @@ public class ProtoLogConfigurationServiceTest { final ProtoLogConfigurationService service = new ProtoLogConfigurationServiceImpl(); Truth.assertThat(service.getGroups()).asList().doesNotContain(TEST_GROUP); - service.enableProtoLogToLogcat(TEST_GROUP); + service.enableProtoLogToLogcat(Mockito.mock(PrintWriter.class), TEST_GROUP); Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue(); final RegisterClientArgs args = new RegisterClientArgs(); diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java index 1273826c8ef8..8cd89ce89e83 100644 --- a/tests/testables/src/android/testing/TestableLooper.java +++ b/tests/testables/src/android/testing/TestableLooper.java @@ -273,7 +273,7 @@ public class TestableLooper { messages.add(message); } - // Repost all Messages back to the queuewith a new time. + // Repost all Messages back to the queue with a new time. while (true) { Message message = messages.poll(); if (message == null) { |