diff options
123 files changed, 2539 insertions, 1464 deletions
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS index f7d2afba428e..e1c45d98e678 100644 --- a/core/java/android/app/OWNERS +++ b/core/java/android/app/OWNERS @@ -63,10 +63,11 @@ per-file *Locale* = file:/services/core/java/com/android/server/locales/OWNERS # Multiuser per-file *User* = file:/MULTIUSER_OWNERS -# Notification, DND, Status bar +# Notification, DND, Status bar, UiModeManager per-file *Notification* = file:/packages/SystemUI/OWNERS per-file *Zen* = file:/packages/SystemUI/OWNERS per-file *StatusBar* = file:/packages/SystemUI/OWNERS +per-file *UiModeManager* = file:/packages/SystemUI/OWNERS # PackageManager per-file ApplicationPackageManager.java = file:/services/core/java/com/android/server/pm/OWNERS diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java index d90257a69281..0ccb9cddf58d 100644 --- a/core/java/android/app/UiModeManager.java +++ b/core/java/android/app/UiModeManager.java @@ -315,7 +315,7 @@ public class UiModeManager { @SystemApi public static final int MODE_NIGHT_CUSTOM_TYPE_BEDTIME = 1; - private IUiModeManager mService; + private static Globals sGlobals; /** * Context required for getting the opPackageName of API caller; maybe be {@code null} if the @@ -341,6 +341,60 @@ public class UiModeManager { mOnProjectionStateChangedListenerResourceManager = new OnProjectionStateChangedListenerResourceManager(); + private static class Globals extends IUiModeManagerCallback.Stub { + + private final IUiModeManager mService; + private final Object mGlobalsLock = new Object(); + + private float mContrast = ContrastUtils.CONTRAST_DEFAULT_VALUE; + + /** + * Map that stores user provided {@link ContrastChangeListener} callbacks, + * and the executors on which these callbacks should be called. + */ + private final ArrayMap<ContrastChangeListener, Executor> + mContrastChangeListeners = new ArrayMap<>(); + + Globals(IUiModeManager service) { + mService = service; + try { + mService.addCallback(this); + mContrast = mService.getContrast(); + } catch (RemoteException e) { + Log.e(TAG, "Setup failed: UiModeManagerService is dead", e); + } + } + + private float getContrast() { + synchronized (mGlobalsLock) { + return mContrast; + } + } + + private void addContrastChangeListener(ContrastChangeListener listener, Executor executor) { + synchronized (mGlobalsLock) { + mContrastChangeListeners.put(listener, executor); + } + } + + private void removeContrastChangeListener(ContrastChangeListener listener) { + synchronized (mGlobalsLock) { + mContrastChangeListeners.remove(listener); + } + } + + @Override + public void notifyContrastChanged(float contrast) { + synchronized (mGlobalsLock) { + // if value changed in the settings, update the cached value and notify listeners + if (Math.abs(mContrast - contrast) < 1e-10) return; + mContrast = contrast; + mContrastChangeListeners.forEach((listener, executor) -> executor.execute( + () -> listener.onContrastChanged(contrast))); + } + } + } + /** * Define constants and conversions between {@link ContrastLevel}s and contrast values. * <p> @@ -407,43 +461,18 @@ public class UiModeManager { } } - /** - * Map that stores user provided {@link ContrastChangeListener} callbacks, - * and the executors on which these callbacks should be called. - */ - private final ArrayMap<ContrastChangeListener, Executor> - mContrastChangeListeners = new ArrayMap<>(); - private float mContrast; - - private final IUiModeManagerCallback.Stub mCallback = new IUiModeManagerCallback.Stub() { - @Override - public void notifyContrastChanged(float contrast) { - final ArrayMap<ContrastChangeListener, Executor> listeners; - synchronized (mLock) { - // if value changed in the settings, update the cached value and notify listeners - if (Math.abs(mContrast - contrast) < 1e-10) return; - mContrast = contrast; - listeners = new ArrayMap<>(mContrastChangeListeners); - } - listeners.forEach((listener, executor) -> executor.execute( - () -> listener.onContrastChanged(mContrast))); - } - }; - @UnsupportedAppUsage /*package*/ UiModeManager() throws ServiceNotFoundException { this(null /* context */); } /*package*/ UiModeManager(Context context) throws ServiceNotFoundException { - mService = IUiModeManager.Stub.asInterface( + IUiModeManager service = IUiModeManager.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.UI_MODE_SERVICE)); mContext = context; - try { - mService.addCallback(mCallback); - mContrast = mService.getContrast(); - } catch (RemoteException e) { - Log.e(TAG, "Setup failed: UiModeManagerService is dead", e); + if (service == null) return; + synchronized (mLock) { + if (sGlobals == null) sGlobals = new Globals(service); } } @@ -533,9 +562,9 @@ public class UiModeManager { @SystemApi @RequiresPermission(android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED) public void enableCarMode(@IntRange(from = 0) int priority, @EnableCarMode int flags) { - if (mService != null) { + if (sGlobals != null) { try { - mService.enableCarMode(flags, priority, + sGlobals.mService.enableCarMode(flags, priority, mContext == null ? null : mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -585,9 +614,9 @@ public class UiModeManager { * @param flags One of the disable car mode flags. */ public void disableCarMode(@DisableCarMode int flags) { - if (mService != null) { + if (sGlobals != null) { try { - mService.disableCarModeByCallingPackage(flags, + sGlobals.mService.disableCarModeByCallingPackage(flags, mContext == null ? null : mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -606,9 +635,9 @@ public class UiModeManager { * {@link Configuration#UI_MODE_TYPE_VR_HEADSET Configuration.UI_MODE_TYPE_VR_HEADSET}. */ public int getCurrentModeType() { - if (mService != null) { + if (sGlobals != null) { try { - return mService.getCurrentModeType(); + return sGlobals.mService.getCurrentModeType(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -653,9 +682,9 @@ public class UiModeManager { * @see #setApplicationNightMode(int) */ public void setNightMode(@NightMode int mode) { - if (mService != null) { + if (sGlobals != null) { try { - mService.setNightMode(mode); + sGlobals.mService.setNightMode(mode); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -674,9 +703,9 @@ public class UiModeManager { @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) public void setNightModeCustomType(@NightModeCustomType int nightModeCustomType) { - if (mService != null) { + if (sGlobals != null) { try { - mService.setNightModeCustomType(nightModeCustomType); + sGlobals.mService.setNightModeCustomType(nightModeCustomType); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -693,9 +722,9 @@ public class UiModeManager { @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) public @NightModeCustomReturnType int getNightModeCustomType() { - if (mService != null) { + if (sGlobals != null) { try { - return mService.getNightModeCustomType(); + return sGlobals.mService.getNightModeCustomType(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -732,9 +761,9 @@ public class UiModeManager { * @see #setNightMode(int) */ public void setApplicationNightMode(@NightMode int mode) { - if (mService != null) { + if (sGlobals != null) { try { - mService.setApplicationNightMode(mode); + sGlobals.mService.setApplicationNightMode(mode); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -757,9 +786,9 @@ public class UiModeManager { * @see #setNightMode(int) */ public @NightMode int getNightMode() { - if (mService != null) { + if (sGlobals != null) { try { - return mService.getNightMode(); + return sGlobals.mService.getNightMode(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -774,9 +803,9 @@ public class UiModeManager { */ @TestApi public boolean isUiModeLocked() { - if (mService != null) { + if (sGlobals != null) { try { - return mService.isUiModeLocked(); + return sGlobals.mService.isUiModeLocked(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -796,9 +825,9 @@ public class UiModeManager { */ @TestApi public boolean isNightModeLocked() { - if (mService != null) { + if (sGlobals != null) { try { - return mService.isNightModeLocked(); + return sGlobals.mService.isNightModeLocked(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -820,9 +849,10 @@ public class UiModeManager { @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) public boolean setNightModeActivatedForCustomMode(@NightModeCustomType int nightModeCustomType, boolean active) { - if (mService != null) { + if (sGlobals != null) { try { - return mService.setNightModeActivatedForCustomMode(nightModeCustomType, active); + return sGlobals.mService.setNightModeActivatedForCustomMode( + nightModeCustomType, active); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -838,9 +868,9 @@ public class UiModeManager { */ @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) public boolean setNightModeActivated(boolean active) { - if (mService != null) { + if (sGlobals != null) { try { - return mService.setNightModeActivated(active); + return sGlobals.mService.setNightModeActivated(active); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -856,9 +886,9 @@ public class UiModeManager { */ @NonNull public LocalTime getCustomNightModeStart() { - if (mService != null) { + if (sGlobals != null) { try { - return LocalTime.ofNanoOfDay(mService.getCustomNightModeStart() * 1000); + return LocalTime.ofNanoOfDay(sGlobals.mService.getCustomNightModeStart() * 1000); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -874,9 +904,9 @@ public class UiModeManager { * @param time The time of the day Dark theme should activate */ public void setCustomNightModeStart(@NonNull LocalTime time) { - if (mService != null) { + if (sGlobals != null) { try { - mService.setCustomNightModeStart(time.toNanoOfDay() / 1000); + sGlobals.mService.setCustomNightModeStart(time.toNanoOfDay() / 1000); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -891,9 +921,9 @@ public class UiModeManager { */ @NonNull public LocalTime getCustomNightModeEnd() { - if (mService != null) { + if (sGlobals != null) { try { - return LocalTime.ofNanoOfDay(mService.getCustomNightModeEnd() * 1000); + return LocalTime.ofNanoOfDay(sGlobals.mService.getCustomNightModeEnd() * 1000); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -909,9 +939,9 @@ public class UiModeManager { * @param time The time of the day Dark theme should deactivate */ public void setCustomNightModeEnd(@NonNull LocalTime time) { - if (mService != null) { + if (sGlobals != null) { try { - mService.setCustomNightModeEnd(time.toNanoOfDay() / 1000); + sGlobals.mService.setCustomNightModeEnd(time.toNanoOfDay() / 1000); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -976,9 +1006,9 @@ public class UiModeManager { @RequiresPermission(value = android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION, conditional = true) public boolean requestProjection(@ProjectionType int projectionType) { - if (mService != null) { + if (sGlobals != null) { try { - return mService.requestProjection(new Binder(), projectionType, + return sGlobals.mService.requestProjection(new Binder(), projectionType, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1005,9 +1035,10 @@ public class UiModeManager { @RequiresPermission(value = android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION, conditional = true) public boolean releaseProjection(@ProjectionType int projectionType) { - if (mService != null) { + if (sGlobals != null) { try { - return mService.releaseProjection(projectionType, mContext.getOpPackageName()); + return sGlobals.mService.releaseProjection( + projectionType, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1028,9 +1059,9 @@ public class UiModeManager { @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE) @NonNull public Set<String> getProjectingPackages(@ProjectionType int projectionType) { - if (mService != null) { + if (sGlobals != null) { try { - return new ArraySet<>(mService.getProjectingPackages(projectionType)); + return new ArraySet<>(sGlobals.mService.getProjectingPackages(projectionType)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1046,9 +1077,9 @@ public class UiModeManager { @SystemApi @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE) public @ProjectionType int getActiveProjectionTypes() { - if (mService != null) { + if (sGlobals != null) { try { - return mService.getActiveProjectionTypes(); + return sGlobals.mService.getActiveProjectionTypes(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1076,11 +1107,12 @@ public class UiModeManager { Slog.i(TAG, "Attempted to add listener that was already added."); return; } - if (mService != null) { + if (sGlobals != null) { InnerListener innerListener = new InnerListener(executor, listener, mOnProjectionStateChangedListenerResourceManager); try { - mService.addOnProjectionStateChangedListener(innerListener, projectionType); + sGlobals.mService.addOnProjectionStateChangedListener( + innerListener, projectionType); mProjectionStateListenerMap.put(listener, innerListener); } catch (RemoteException e) { mOnProjectionStateChangedListenerResourceManager.remove(innerListener); @@ -1107,9 +1139,9 @@ public class UiModeManager { Slog.i(TAG, "Attempted to remove listener that was not added."); return; } - if (mService != null) { + if (sGlobals != null) { try { - mService.removeOnProjectionStateChangedListener(innerListener); + sGlobals.mService.removeOnProjectionStateChangedListener(innerListener); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1197,15 +1229,10 @@ public class UiModeManager { * <li> -1 corresponds to the minimum contrast </li> * <li> 1 corresponds to the maximum contrast </li> * </ul> - * - * - * */ @FloatRange(from = -1.0f, to = 1.0f) public float getContrast() { - synchronized (mLock) { - return mContrast; - } + return sGlobals.getContrast(); } /** @@ -1219,9 +1246,7 @@ public class UiModeManager { @NonNull ContrastChangeListener listener) { Objects.requireNonNull(executor); Objects.requireNonNull(listener); - synchronized (mLock) { - mContrastChangeListeners.put(listener, executor); - } + sGlobals.addContrastChangeListener(listener, executor); } /** @@ -1232,8 +1257,6 @@ public class UiModeManager { */ public void removeContrastChangeListener(@NonNull ContrastChangeListener listener) { Objects.requireNonNull(listener); - synchronized (mLock) { - mContrastChangeListeners.remove(listener); - } + sGlobals.removeContrastChangeListener(listener); } } diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java index 93c0c8a4a47a..dd311755bf3f 100644 --- a/core/java/android/app/admin/PasswordMetrics.java +++ b/core/java/android/app/admin/PasswordMetrics.java @@ -179,17 +179,11 @@ public final class PasswordMetrics implements Parcelable { }; /** - * Returns the {@code PasswordMetrics} for a given credential. - * - * If the credential is a pin or a password, equivalent to - * {@link #computeForPasswordOrPin(byte[], boolean)}. {@code credential} cannot be null - * when {@code type} is - * {@link com.android.internal.widget.LockPatternUtils#CREDENTIAL_TYPE_PASSWORD}. + * Returns the {@code PasswordMetrics} for the given credential. */ public static PasswordMetrics computeForCredential(LockscreenCredential credential) { if (credential.isPassword() || credential.isPin()) { - return PasswordMetrics.computeForPasswordOrPin(credential.getCredential(), - credential.isPin()); + return computeForPasswordOrPin(credential.getCredential(), credential.isPin()); } else if (credential.isPattern()) { PasswordMetrics metrics = new PasswordMetrics(CREDENTIAL_TYPE_PATTERN); metrics.length = credential.size(); @@ -202,10 +196,10 @@ public final class PasswordMetrics implements Parcelable { } /** - * Returns the {@code PasswordMetrics} for a given password or pin + * Returns the {@code PasswordMetrics} for the given password or pin. */ - public static PasswordMetrics computeForPasswordOrPin(byte[] password, boolean isPin) { - // Analyse the characters used + private static PasswordMetrics computeForPasswordOrPin(byte[] credential, boolean isPin) { + // Analyze the characters used. int letters = 0; int upperCase = 0; int lowerCase = 0; @@ -213,8 +207,8 @@ public final class PasswordMetrics implements Parcelable { int symbols = 0; int nonLetter = 0; int nonNumeric = 0; - final int length = password.length; - for (byte b : password) { + final int length = credential.length; + for (byte b : credential) { switch (categoryChar((char) b)) { case CHAR_LOWER_CASE: letters++; @@ -239,7 +233,7 @@ public final class PasswordMetrics implements Parcelable { } final int credType = isPin ? CREDENTIAL_TYPE_PIN : CREDENTIAL_TYPE_PASSWORD; - final int seqLength = maxLengthSequence(password); + final int seqLength = maxLengthSequence(credential); return new PasswordMetrics(credType, length, letters, upperCase, lowerCase, numeric, symbols, nonLetter, nonNumeric, seqLength); } diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java index c0877d3ad8e2..e886f685263c 100644 --- a/core/java/android/hardware/input/InputManagerGlobal.java +++ b/core/java/android/hardware/input/InputManagerGlobal.java @@ -54,6 +54,7 @@ import android.view.InputMonitor; import android.view.PointerIcon; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SomeArgs; import java.util.ArrayList; @@ -140,23 +141,27 @@ public final class InputManagerGlobal { } /** - * Gets an instance of the input manager. - * - * @return The input manager instance. + * A test session tracker for InputManagerGlobal. + * @see #createTestSession(IInputManager) */ - public static InputManagerGlobal resetInstance(IInputManager inputManagerService) { - synchronized (InputManager.class) { - sInstance = new InputManagerGlobal(inputManagerService); - return sInstance; - } + @VisibleForTesting + public interface TestSession extends AutoCloseable { + @Override + void close(); } /** - * Clear the instance of the input manager. + * Create and set a test instance of InputManagerGlobal. + * + * @return The test session. The session must be {@link TestSession#close()}-ed at the end + * of the test. */ - public static void clearInstance() { + @VisibleForTesting + public static TestSession createTestSession(IInputManager inputManagerService) { synchronized (InputManagerGlobal.class) { - sInstance = null; + final var oldInstance = sInstance; + sInstance = new InputManagerGlobal(inputManagerService); + return () -> sInstance = oldInstance; } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index bf8c0d274fc8..9f1cc7184bc3 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -32104,9 +32104,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return false; } - getLocationInWindow(mAttachInfo.mTmpLocation); - return mAttachInfo.mTmpLocation[0] == insets.getStableInsetLeft() - && mAttachInfo.mTmpLocation[1] == insets.getStableInsetTop(); + return true; } /** diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java index afc567e5de5c..c88048c17160 100644 --- a/core/java/android/view/WindowManagerPolicyConstants.java +++ b/core/java/android/view/WindowManagerPolicyConstants.java @@ -97,6 +97,12 @@ public interface WindowManagerPolicyConstants { */ String EXTRA_START_REASON = "android.intent.extra.EXTRA_START_REASON"; + /** + * Set to {@code true} when intent was invoked from pressing one of the brightness keys. + * @hide + */ + String EXTRA_FROM_BRIGHTNESS_KEY = "android.intent.extra.FROM_BRIGHTNESS_KEY"; + // TODO: move this to a more appropriate place. interface PointerEventListener { /** diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index e098b6326475..a43906f30ff7 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -192,6 +192,8 @@ import java.util.List; * <li>{@link #getEventTime()} - The event time.</li> * <li>{@link #getScrollDeltaX()} - The difference in the horizontal position.</li> * <li>{@link #getScrollDeltaY()} - The difference in the vertical position.</li> + * <li>{@link #getMaxScrollX()} ()} - The max scroll offset of the source left edge</li> + * <li>{@link #getMaxScrollY()} ()} - The max scroll offset of the source top edge.</li> * </ul> * </p> * <p> @@ -538,8 +540,18 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par public static final int TYPE_WINDOW_CONTENT_CHANGED = 1 << 11; /** - * Represents the event of scrolling a view. This event type is generally not sent directly. - * @see android.view.View#onScrollChanged(int, int, int, int) + * Represents the event of scrolling a view. This event type is generally not sent directly. In + * the View system, this is sent in + * {@link android.view.View#onScrollChanged(int, int, int, int)} + * <p>In addition to the source and package name, the event should populate scroll-specific + * properties like {@link #setScrollDeltaX(int)}, {@link #setScrollDeltaY(int)}, + * {@link #setMaxScrollX(int)}, and {@link #setMaxScrollY(int)}. + * <p>Services are encouraged to rely on the source to query UI state over AccessibilityEvents + * properties. For example, to check after a scroll if the bottom of the scrolling UI element + * has been reached, check if the source node is scrollable and has the + * {@link AccessibilityNodeInfo.AccessibilityAction#ACTION_SCROLL_BACKWARD} action but not the + * {@link AccessibilityNodeInfo.AccessibilityAction#ACTION_SCROLL_FORWARD} action. + * For scrolling to a target, use {@link #TYPE_VIEW_TARGETED_BY_SCROLL}. */ public static final int TYPE_VIEW_SCROLLED = 1 << 12; diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 293995416a16..e6a8b7827b04 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -425,11 +425,13 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Action to scroll the node content forward. + * @see AccessibilityAction#ACTION_SCROLL_FORWARD */ public static final int ACTION_SCROLL_FORWARD = 1 << 12; /** * Action to scroll the node content backward. + * @see AccessibilityAction#ACTION_SCROLL_BACKWARD */ public static final int ACTION_SCROLL_BACKWARD = 1 << 13; @@ -5404,6 +5406,42 @@ public class AccessibilityNodeInfo implements Parcelable { * <strong>Arguments:</strong> * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument. * </p> + * <p>The UI element that implements this should send a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. Depending on the orientation, + * this element should also add the relevant directional scroll actions of + * {@link #ACTION_SCROLL_LEFT}, {@link #ACTION_SCROLL_RIGHT}, + * {@link #ACTION_SCROLL_UP}, and {@link #ACTION_SCROLL_DOWN}. If the scrolling brings + * the next or previous element into view as the center element, such as in a ViewPager2, + * use {@link #ACTION_PAGE_DOWN} and the other page actions instead of the directional + * actions. + * <p>Example: a scrolling UI of vertical orientation with a forward + * scroll action should also add the scroll down action: + * <pre class="prettyprint"><code> + * onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + * super.onInitializeAccessibilityNodeInfo(info); + * if (canScrollForward) { + * info.addAction(ACTION_SCROLL_FORWARD); + * info.addAction(ACTION_SCROLL_DOWN); + * } + * } + * performAccessibilityAction(int action, Bundle bundle) { + * if (action == ACTION_SCROLL_FORWARD || action == ACTION_SCROLL_DOWN) { + * scrollForward(); + * } + * } + * scrollForward() { + * ... + * if (mAccessibilityManager.isEnabled()) { + * event = new AccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED); + * event.setScrollDeltaX(dx); + * event.setScrollDeltaY(dy); + * event.setMaxScrollX(maxDx); + * event.setMaxScrollY(maxDY); + * sendAccessibilityEventUnchecked(event); + * } + * } + * </code> + * </pre></p> */ public static final AccessibilityAction ACTION_SCROLL_FORWARD = new AccessibilityAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); @@ -5414,6 +5452,54 @@ public class AccessibilityNodeInfo implements Parcelable { * <strong>Arguments:</strong> * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument. * </p> + * <p>The UI element that implements this should send a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. Depending on the orientation, + * this element should also add the relevant directional scroll actions of + * {@link #ACTION_SCROLL_LEFT}, {@link #ACTION_SCROLL_RIGHT}, + * {@link #ACTION_SCROLL_UP}, and {@link #ACTION_SCROLL_DOWN}. If the scrolling brings + * the next or previous element into view as the center element, such as in a ViewPager2, + * use {@link #ACTION_PAGE_DOWN} and the other page actions instead of the directional + * actions. + * <p> Example: a scrolling UI of horizontal orientation with a backward + * scroll action should also add the scroll left/right action (LTR/RTL): + * <pre class="prettyprint"><code> + * onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + * super.onInitializeAccessibilityNodeInfo(info); + * if (canScrollBackward) { + * info.addAction(ACTION_SCROLL_FORWARD); + * if (leftToRight) { + * info.addAction(ACTION_SCROLL_LEFT); + * } else { + * info.addAction(ACTION_SCROLL_RIGHT); + * } + * } + * } + * performAccessibilityAction(int action, Bundle bundle) { + * if (action == ACTION_SCROLL_BACKWARD) { + * scrollBackward(); + * } else if (action == ACTION_SCROLL_LEFT) { + * if (!isRTL()){ + * scrollBackward(); + * } + * } else if (action == ACTION_SCROLL_RIGHT) { + * if (isRTL()){ + * scrollBackward(); + * } + * } + * } + * scrollBackward() { + * ... + * if (mAccessibilityManager.isEnabled()) { + * event = new AccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED); + * event.setScrollDeltaX(dx); + * event.setScrollDeltaY(dy); + * event.setMaxScrollX(maxDx); + * event.setMaxScrollY(maxDY); + * sendAccessibilityEventUnchecked(event); + * } + * } + * </code> + * </pre></p> */ public static final AccessibilityAction ACTION_SCROLL_BACKWARD = new AccessibilityAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD); @@ -5509,6 +5595,8 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Action that requests the node make its bounding rectangle visible * on the screen, scrolling if necessary just enough. + * <p>The UI element that implements this should send a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. * * @see View#requestRectangleOnScreen(Rect) */ @@ -5524,6 +5612,8 @@ public class AccessibilityNodeInfo implements Parcelable { * <li>{@link AccessibilityNodeInfo#ACTION_ARGUMENT_ROW_INT}</li> * <li>{@link AccessibilityNodeInfo#ACTION_ARGUMENT_COLUMN_INT}</li> * <ul> + * <p>The UI element that implements this should send a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. * * @see AccessibilityNodeInfo#getCollectionInfo() */ @@ -5562,6 +5652,8 @@ public class AccessibilityNodeInfo implements Parcelable { * <strong>Arguments:</strong> * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument. * </p> + * <p>The UI element that implements this should send a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. */ public static final AccessibilityAction ACTION_SCROLL_UP = new AccessibilityAction(R.id.accessibilityActionScrollUp); @@ -5572,6 +5664,8 @@ public class AccessibilityNodeInfo implements Parcelable { * <strong>Arguments:</strong> * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument. * </p> + * <p>The UI element that implements this should send a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. */ public static final AccessibilityAction ACTION_SCROLL_LEFT = new AccessibilityAction(R.id.accessibilityActionScrollLeft); @@ -5582,6 +5676,8 @@ public class AccessibilityNodeInfo implements Parcelable { * <strong>Arguments:</strong> * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument. * </p> + * <p>The UI element that implements this should send a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. */ public static final AccessibilityAction ACTION_SCROLL_DOWN = new AccessibilityAction(R.id.accessibilityActionScrollDown); @@ -5592,30 +5688,40 @@ public class AccessibilityNodeInfo implements Parcelable { * <strong>Arguments:</strong> * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument. * </p> + * <p>The UI element that implements this should send a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. */ public static final AccessibilityAction ACTION_SCROLL_RIGHT = new AccessibilityAction(R.id.accessibilityActionScrollRight); /** * Action to move to the page above. + * <p>The UI element that implements this should send a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. */ public static final AccessibilityAction ACTION_PAGE_UP = new AccessibilityAction(R.id.accessibilityActionPageUp); /** * Action to move to the page below. + * <p>The UI element that implements this should send a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. */ public static final AccessibilityAction ACTION_PAGE_DOWN = new AccessibilityAction(R.id.accessibilityActionPageDown); /** * Action to move to the page left. + * <p>The UI element that implements this should send a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. */ public static final AccessibilityAction ACTION_PAGE_LEFT = new AccessibilityAction(R.id.accessibilityActionPageLeft); /** * Action to move to the page right. + * <p>The UI element that implements this should send a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. */ public static final AccessibilityAction ACTION_PAGE_RIGHT = new AccessibilityAction(R.id.accessibilityActionPageRight); @@ -5720,8 +5826,9 @@ public class AccessibilityNodeInfo implements Parcelable { * This action initiates a drag & drop within the system. The source's dragged content is * prepared before the drag begins. In View, this action should prepare the arguments to * {@link View#startDragAndDrop(ClipData, View.DragShadowBuilder, Object, int)} and then - * call {@link View#startDragAndDrop(ClipData, View.DragShadowBuilder, Object, int)}. The - * equivalent should be performed for other UI toolkits. + * call {@link View#startDragAndDrop(ClipData, View.DragShadowBuilder, Object, int)} with + * {@link View#DRAG_FLAG_ACCESSIBILITY_ACTION}. The equivalent should be performed for other + * UI toolkits. * </p> * * @see AccessibilityEvent#CONTENT_CHANGE_TYPE_DRAG_STARTED @@ -5735,7 +5842,8 @@ public class AccessibilityNodeInfo implements Parcelable { * This action is added to potential drop targets if the source started a drag with * {@link #ACTION_DRAG_START}. In View, these targets are Views that accepted * {@link android.view.DragEvent#ACTION_DRAG_STARTED} and have an - * {@link View.OnDragListener}. + * {@link View.OnDragListener}, and the drop occurs at the center location of the View's + * window bounds. * </p> * * @see AccessibilityEvent#CONTENT_CHANGE_TYPE_DRAG_DROPPED diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index c9afdc0ad074..2c7d326587c7 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -414,7 +414,7 @@ public final class ContentCaptureManager { /** @hide */ public static final boolean DEFAULT_ENABLE_CONTENT_PROTECTION_RECEIVER = false; /** @hide */ - public static final int DEFAULT_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE = 1000; + public static final int DEFAULT_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE = 5000; /** @hide */ public static final int DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE = 150; diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index e1be0cd80bb6..0b51a4112ba6 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -30,6 +30,8 @@ #include <media/AudioSystem.h> #include <nativehelper/JNIHelp.h> #include <nativehelper/ScopedLocalRef.h> +#include <nativehelper/ScopedPrimitiveArray.h> +#include <nativehelper/jni_macros.h> #include <system/audio.h> #include <system/audio_policy.h> #include <utils/Log.h> @@ -285,7 +287,7 @@ JNIAudioPortCallback::JNIAudioPortCallback(JNIEnv* env, jobject thiz, jobject we ALOGE("Can't find class %s", kEventHandlerClassPathName); return; } - mClass = (jclass)env->NewGlobalRef(clazz); + mClass = static_cast<jclass>(env->NewGlobalRef(clazz)); // We use a weak reference so the AudioPortEventHandler object can be garbage collected. // The reference is only used as a proxy for callbacks. @@ -337,15 +339,16 @@ static sp<JNIAudioPortCallback> setJniCallback(JNIEnv* env, const sp<JNIAudioPortCallback>& callback) { Mutex::Autolock l(gLock); - sp<JNIAudioPortCallback> old = - (JNIAudioPortCallback*)env->GetLongField(thiz, gEventHandlerFields.mJniCallback); + sp<JNIAudioPortCallback> old = reinterpret_cast<JNIAudioPortCallback *>( + env->GetLongField(thiz, gEventHandlerFields.mJniCallback)); if (callback.get()) { - callback->incStrong((void*)setJniCallback); + callback->incStrong(reinterpret_cast<void *>(setJniCallback)); } if (old != 0) { - old->decStrong((void*)setJniCallback); + old->decStrong(reinterpret_cast<void *>(setJniCallback)); } - env->SetLongField(thiz, gEventHandlerFields.mJniCallback, (jlong)callback.get()); + env->SetLongField(thiz, gEventHandlerFields.mJniCallback, + reinterpret_cast<jlong>(callback.get())); return old; } @@ -374,43 +377,44 @@ static jint getVectorOfAudioDeviceTypeAddr(JNIEnv *env, jintArray deviceTypes, jobjectArray deviceAddresses, AudioDeviceTypeAddrVector &audioDeviceTypeAddrVector) { if (deviceTypes == nullptr || deviceAddresses == nullptr) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } jsize deviceCount = env->GetArrayLength(deviceTypes); if (deviceCount == 0 || deviceCount != env->GetArrayLength(deviceAddresses)) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } // retrieve all device types std::vector<audio_devices_t> deviceTypesVector; jint *typesPtr = nullptr; typesPtr = env->GetIntArrayElements(deviceTypes, 0); if (typesPtr == nullptr) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } for (jint i = 0; i < deviceCount; i++) { - deviceTypesVector.push_back((audio_devices_t)typesPtr[i]); + deviceTypesVector.push_back(static_cast<audio_devices_t>(typesPtr[i])); } // check each address is a string and add device type/address to list jclass stringClass = FindClassOrDie(env, "java/lang/String"); for (jint i = 0; i < deviceCount; i++) { jobject addrJobj = env->GetObjectArrayElement(deviceAddresses, i); if (!env->IsInstanceOf(addrJobj, stringClass)) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } - const char *address = env->GetStringUTFChars((jstring)addrJobj, NULL); - AudioDeviceTypeAddr dev = AudioDeviceTypeAddr((audio_devices_t)typesPtr[i], address); + const char *address = env->GetStringUTFChars(static_cast<jstring>(addrJobj), NULL); + AudioDeviceTypeAddr dev = + AudioDeviceTypeAddr(static_cast<audio_devices_t>(typesPtr[i]), address); audioDeviceTypeAddrVector.push_back(dev); - env->ReleaseStringUTFChars((jstring)addrJobj, address); + env->ReleaseStringUTFChars(static_cast<jstring>(addrJobj), address); } env->ReleaseIntArrayElements(deviceTypes, typesPtr, 0); - return (jint)NO_ERROR; + return NO_ERROR; } static jint android_media_AudioSystem_muteMicrophone(JNIEnv *env, jobject thiz, jboolean on) { - return (jint) check_AudioSystem_Command(AudioSystem::muteMicrophone(on)); + return check_AudioSystem_Command(AudioSystem::muteMicrophone(on)); } static jboolean @@ -425,7 +429,7 @@ static jboolean android_media_AudioSystem_isStreamActive(JNIEnv *env, jobject thiz, jint stream, jint inPastMs) { bool state = false; - AudioSystem::isStreamActive((audio_stream_type_t) stream, &state, inPastMs); + AudioSystem::isStreamActive(static_cast<audio_stream_type_t>(stream), &state, inPastMs); return state; } @@ -434,7 +438,7 @@ android_media_AudioSystem_isStreamActiveRemotely(JNIEnv *env, jobject thiz, jint jint inPastMs) { bool state = false; - AudioSystem::isStreamActiveRemotely((audio_stream_type_t) stream, &state, inPastMs); + AudioSystem::isStreamActiveRemotely(static_cast<audio_stream_type_t>(stream), &state, inPastMs); return state; } @@ -442,7 +446,7 @@ static jboolean android_media_AudioSystem_isSourceActive(JNIEnv *env, jobject thiz, jint source) { bool state = false; - AudioSystem::isSourceActive((audio_source_t) source, &state); + AudioSystem::isSourceActive(static_cast<audio_source_t>(source), &state); return state; } @@ -478,7 +482,7 @@ android_media_AudioSystem_setParameters(JNIEnv *env, jobject thiz, jstring keyVa env->ReleaseStringCritical(keyValuePairs, c_keyValuePairs); } int status = check_AudioSystem_Command(AudioSystem::setParameters(c_keyValuePairs8)); - return (jint) status; + return status; } static jstring @@ -558,15 +562,15 @@ android_media_AudioSystem_recording_callback(int event, return; } jint recParamData[REC_PARAM_SIZE]; - recParamData[0] = (jint) audioFormatFromNative(clientConfig->format); + recParamData[0] = audioFormatFromNative(clientConfig->format); // FIXME this doesn't support index-based masks - recParamData[1] = (jint) inChannelMaskFromNative(clientConfig->channel_mask); - recParamData[2] = (jint) clientConfig->sample_rate; - recParamData[3] = (jint) audioFormatFromNative(deviceConfig->format); + recParamData[1] = inChannelMaskFromNative(clientConfig->channel_mask); + recParamData[2] = clientConfig->sample_rate; + recParamData[3] = audioFormatFromNative(deviceConfig->format); // FIXME this doesn't support index-based masks - recParamData[4] = (jint) inChannelMaskFromNative(deviceConfig->channel_mask); - recParamData[5] = (jint) deviceConfig->sample_rate; - recParamData[6] = (jint) patchHandle; + recParamData[4] = inChannelMaskFromNative(deviceConfig->channel_mask); + recParamData[5] = deviceConfig->sample_rate; + recParamData[6] = patchHandle; env->SetIntArrayRegion(recParamArray, 0, REC_PARAM_SIZE, recParamData); jobjectArray jClientEffects; @@ -580,10 +584,9 @@ android_media_AudioSystem_recording_callback(int event, env->CallStaticVoidMethod(clazz, gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative, - event, (jint) clientInfo->riid, (jint) clientInfo->uid, - clientInfo->session, clientInfo->source, clientInfo->port_id, - clientInfo->silenced, recParamArray, jClientEffects, jEffects, - source); + event, clientInfo->riid, clientInfo->uid, clientInfo->session, + clientInfo->source, clientInfo->port_id, clientInfo->silenced, + recParamArray, jClientEffects, jEffects, source); env->DeleteLocalRef(clazz); env->DeleteLocalRef(recParamArray); env->DeleteLocalRef(jClientEffects); @@ -626,11 +629,9 @@ static jint android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobj if (Parcel *parcel = parcelForJavaObject(env, jParcel); parcel != nullptr) { android::media::audio::common::AudioPort port{}; if (status_t statusOfParcel = port.readFromParcel(parcel); statusOfParcel == OK) { - status = check_AudioSystem_Command( - AudioSystem::setDeviceConnectionState(static_cast<audio_policy_dev_state_t>( - state), - port, - static_cast<audio_format_t>(codec))); + status = check_AudioSystem_Command( + AudioSystem::setDeviceConnectionState(static_cast<audio_policy_dev_state_t>(state), + port, static_cast<audio_format_t>(codec))); } else { ALOGE("Failed to read from parcel: %s", statusToString(statusOfParcel).c_str()); status = kAudioStatusError; @@ -639,17 +640,17 @@ static jint android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobj ALOGE("Failed to retrieve the native parcel from Java parcel"); status = kAudioStatusError; } - return (jint) status; + return status; } static jint android_media_AudioSystem_getDeviceConnectionState(JNIEnv *env, jobject thiz, jint device, jstring device_address) { const char *c_address = env->GetStringUTFChars(device_address, NULL); - int state = static_cast <int>(AudioSystem::getDeviceConnectionState(static_cast <audio_devices_t>(device), - c_address)); + int state = static_cast<int>( + AudioSystem::getDeviceConnectionState(static_cast<audio_devices_t>(device), c_address)); env->ReleaseStringUTFChars(device_address, c_address); - return (jint) state; + return state; } static jint @@ -658,38 +659,41 @@ android_media_AudioSystem_handleDeviceConfigChange(JNIEnv *env, jobject thiz, ji { const char *c_address = env->GetStringUTFChars(device_address, NULL); const char *c_name = env->GetStringUTFChars(device_name, NULL); - int status = check_AudioSystem_Command(AudioSystem::handleDeviceConfigChange(static_cast <audio_devices_t>(device), - c_address, c_name, static_cast <audio_format_t>(codec))); + int status = check_AudioSystem_Command( + AudioSystem::handleDeviceConfigChange(static_cast<audio_devices_t>(device), c_address, + c_name, static_cast<audio_format_t>(codec))); env->ReleaseStringUTFChars(device_address, c_address); env->ReleaseStringUTFChars(device_name, c_name); - return (jint) status; + return status; } static jint android_media_AudioSystem_setPhoneState(JNIEnv *env, jobject thiz, jint state, jint uid) { - return (jint)check_AudioSystem_Command( - AudioSystem::setPhoneState((audio_mode_t)state, (uid_t)uid)); + return check_AudioSystem_Command( + AudioSystem::setPhoneState(static_cast<audio_mode_t>(state), static_cast<uid_t>(uid))); } static jint android_media_AudioSystem_setForceUse(JNIEnv *env, jobject thiz, jint usage, jint config) { - return (jint) check_AudioSystem_Command(AudioSystem::setForceUse(static_cast <audio_policy_force_use_t>(usage), - static_cast <audio_policy_forced_cfg_t>(config))); + return check_AudioSystem_Command( + AudioSystem::setForceUse(static_cast<audio_policy_force_use_t>(usage), + static_cast<audio_policy_forced_cfg_t>(config))); } static jint android_media_AudioSystem_getForceUse(JNIEnv *env, jobject thiz, jint usage) { - return static_cast <jint>(AudioSystem::getForceUse(static_cast <audio_policy_force_use_t>(usage))); + return static_cast<jint>( + AudioSystem::getForceUse(static_cast<audio_policy_force_use_t>(usage))); } static jint android_media_AudioSystem_initStreamVolume(JNIEnv *env, jobject thiz, jint stream, jint indexMin, jint indexMax) { - return (jint) check_AudioSystem_Command(AudioSystem::initStreamVolume(static_cast <audio_stream_type_t>(stream), - indexMin, - indexMax)); + return check_AudioSystem_Command( + AudioSystem::initStreamVolume(static_cast<audio_stream_type_t>(stream), indexMin, + indexMax)); } static jint @@ -699,10 +703,9 @@ android_media_AudioSystem_setStreamVolumeIndex(JNIEnv *env, jint index, jint device) { - return (jint) check_AudioSystem_Command( - AudioSystem::setStreamVolumeIndex(static_cast <audio_stream_type_t>(stream), - index, - (audio_devices_t)device)); + return check_AudioSystem_Command( + AudioSystem::setStreamVolumeIndex(static_cast<audio_stream_type_t>(stream), index, + static_cast<audio_devices_t>(device))); } static jint @@ -712,13 +715,11 @@ android_media_AudioSystem_getStreamVolumeIndex(JNIEnv *env, jint device) { int index; - if (AudioSystem::getStreamVolumeIndex(static_cast <audio_stream_type_t>(stream), - &index, - (audio_devices_t)device) - != NO_ERROR) { + if (AudioSystem::getStreamVolumeIndex(static_cast<audio_stream_type_t>(stream), &index, + static_cast<audio_devices_t>(device)) != NO_ERROR) { index = -1; } - return (jint) index; + return index; } static jint @@ -731,11 +732,12 @@ android_media_AudioSystem_setVolumeIndexForAttributes(JNIEnv *env, // read the AudioAttributes values JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return jStatus; } - return (jint) check_AudioSystem_Command( - AudioSystem::setVolumeIndexForAttributes(*(paa.get()), index, (audio_devices_t)device)); + return check_AudioSystem_Command( + AudioSystem::setVolumeIndexForAttributes(*(paa.get()), index, + static_cast<audio_devices_t>(device))); } static jint @@ -747,15 +749,16 @@ android_media_AudioSystem_getVolumeIndexForAttributes(JNIEnv *env, // read the AudioAttributes values JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return jStatus; } int index; - if (AudioSystem::getVolumeIndexForAttributes(*(paa.get()), index, (audio_devices_t)device) - != NO_ERROR) { + if (AudioSystem::getVolumeIndexForAttributes(*(paa.get()), index, + static_cast<audio_devices_t>(device)) != + NO_ERROR) { index = -1; } - return (jint) index; + return index; } static jint @@ -766,7 +769,7 @@ android_media_AudioSystem_getMinVolumeIndexForAttributes(JNIEnv *env, // read the AudioAttributes values JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return jStatus; } int index; @@ -774,7 +777,7 @@ android_media_AudioSystem_getMinVolumeIndexForAttributes(JNIEnv *env, != NO_ERROR) { index = -1; } - return (jint) index; + return index; } static jint @@ -785,7 +788,7 @@ android_media_AudioSystem_getMaxVolumeIndexForAttributes(JNIEnv *env, // read the AudioAttributes values JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return jStatus; } int index; @@ -793,13 +796,13 @@ android_media_AudioSystem_getMaxVolumeIndexForAttributes(JNIEnv *env, != NO_ERROR) { index = -1; } - return (jint) index; + return index; } static jint android_media_AudioSystem_setMasterVolume(JNIEnv *env, jobject thiz, jfloat value) { - return (jint) check_AudioSystem_Command(AudioSystem::setMasterVolume(value)); + return check_AudioSystem_Command(AudioSystem::setMasterVolume(value)); } static jfloat @@ -815,7 +818,7 @@ android_media_AudioSystem_getMasterVolume(JNIEnv *env, jobject thiz) static jint android_media_AudioSystem_setMasterMute(JNIEnv *env, jobject thiz, jboolean mute) { - return (jint) check_AudioSystem_Command(AudioSystem::setMasterMute(mute)); + return check_AudioSystem_Command(AudioSystem::setMasterMute(mute)); } static jboolean @@ -831,7 +834,7 @@ android_media_AudioSystem_getMasterMute(JNIEnv *env, jobject thiz) static jint android_media_AudioSystem_setMasterMono(JNIEnv *env, jobject thiz, jboolean mono) { - return (jint) check_AudioSystem_Command(AudioSystem::setMasterMono(mono)); + return check_AudioSystem_Command(AudioSystem::setMasterMono(mono)); } static jboolean @@ -847,7 +850,7 @@ android_media_AudioSystem_getMasterMono(JNIEnv *env, jobject thiz) static jint android_media_AudioSystem_setMasterBalance(JNIEnv *env, jobject thiz, jfloat balance) { - return (jint) check_AudioSystem_Command(AudioSystem::setMasterBalance(balance)); + return check_AudioSystem_Command(AudioSystem::setMasterBalance(balance)); } static jfloat @@ -865,37 +868,37 @@ android_media_AudioSystem_getMasterBalance(JNIEnv *env, jobject thiz) static jint android_media_AudioSystem_getPrimaryOutputSamplingRate(JNIEnv *env, jobject clazz) { - return (jint) AudioSystem::getPrimaryOutputSamplingRate(); + return AudioSystem::getPrimaryOutputSamplingRate(); } static jint android_media_AudioSystem_getPrimaryOutputFrameCount(JNIEnv *env, jobject clazz) { - return (jint) AudioSystem::getPrimaryOutputFrameCount(); + return AudioSystem::getPrimaryOutputFrameCount(); } static jint android_media_AudioSystem_getOutputLatency(JNIEnv *env, jobject clazz, jint stream) { uint32_t afLatency; - if (AudioSystem::getOutputLatency(&afLatency, static_cast <audio_stream_type_t>(stream)) - != NO_ERROR) { + if (AudioSystem::getOutputLatency(&afLatency, static_cast<audio_stream_type_t>(stream)) != + NO_ERROR) { afLatency = -1; } - return (jint) afLatency; + return afLatency; } static jint android_media_AudioSystem_setLowRamDevice( JNIEnv *env, jobject clazz, jboolean isLowRamDevice, jlong totalMemory) { - return (jint) AudioSystem::setLowRamDevice((bool) isLowRamDevice, (int64_t) totalMemory); + return AudioSystem::setLowRamDevice(isLowRamDevice, totalMemory); } static jint android_media_AudioSystem_checkAudioFlinger(JNIEnv *env, jobject clazz) { - return (jint) check_AudioSystem_Command(AudioSystem::checkAudioFlinger()); + return check_AudioSystem_Command(AudioSystem::checkAudioFlinger()); } static void android_media_AudioSystem_setAudioFlingerBinder(JNIEnv *env, jobject clazz, @@ -909,8 +912,8 @@ static void convertAudioGainConfigToNative(JNIEnv *env, bool useInMask) { nAudioGainConfig->index = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mIndex); - nAudioGainConfig->mode = - (audio_gain_mode_t)env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mMode); + nAudioGainConfig->mode = static_cast<audio_gain_mode_t>( + env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mMode)); ALOGV("convertAudioGainConfigToNative got gain index %d", nAudioGainConfig->index); jint jMask = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mChannelMask); audio_channel_mask_t nMask; @@ -924,8 +927,8 @@ static void convertAudioGainConfigToNative(JNIEnv *env, nAudioGainConfig->channel_mask = nMask; nAudioGainConfig->ramp_duration_ms = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mRampDurationMs); - jintArray jValues = (jintArray)env->GetObjectField(jAudioGainConfig, - gAudioGainConfigFields.mValues); + jintArray jValues = static_cast<jintArray>( + env->GetObjectField(jAudioGainConfig, gAudioGainConfigFields.mValues)); int *nValues = env->GetIntArrayElements(jValues, NULL); size_t size = env->GetArrayLength(jValues); memcpy(nAudioGainConfig->values, nValues, size * sizeof(int)); @@ -940,8 +943,8 @@ static jint convertAudioPortConfigToNative(JNIEnv *env, jobject jAudioPort = env->GetObjectField(jAudioPortConfig, gAudioPortConfigFields.mPort); jobject jHandle = env->GetObjectField(jAudioPort, gAudioPortFields.mHandle); nAudioPortConfig->id = env->GetIntField(jHandle, gAudioHandleFields.mId); - nAudioPortConfig->role = (audio_port_role_t)env->GetIntField(jAudioPort, - gAudioPortFields.mRole); + nAudioPortConfig->role = + static_cast<audio_port_role_t>(env->GetIntField(jAudioPort, gAudioPortFields.mRole)); if (env->IsInstanceOf(jAudioPort, gAudioDevicePortClass)) { nAudioPortConfig->type = AUDIO_PORT_TYPE_DEVICE; } else if (env->IsInstanceOf(jAudioPort, gAudioMixPortClass)) { @@ -949,7 +952,7 @@ static jint convertAudioPortConfigToNative(JNIEnv *env, } else { env->DeleteLocalRef(jAudioPort); env->DeleteLocalRef(jHandle); - return (jint)AUDIO_JAVA_ERROR; + return AUDIO_JAVA_ERROR; } ALOGV("convertAudioPortConfigToNative handle %d role %d type %d", nAudioPortConfig->id, nAudioPortConfig->role, nAudioPortConfig->type); @@ -1004,7 +1007,7 @@ static jint convertAudioPortConfigToNative(JNIEnv *env, } env->DeleteLocalRef(jAudioPort); env->DeleteLocalRef(jHandle); - return (jint)AUDIO_JAVA_SUCCESS; + return AUDIO_JAVA_SUCCESS; } /** @@ -1025,15 +1028,15 @@ static jint convertAudioPortConfigToNativeWithDevicePort(JNIEnv *env, } // Supports AUDIO_PORT_TYPE_DEVICE only if (nAudioPortConfig->type != AUDIO_PORT_TYPE_DEVICE) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } jobject jAudioDevicePort = env->GetObjectField(jAudioPortConfig, gAudioPortConfigFields.mPort); - nAudioPortConfig->ext.device.type = - (audio_devices_t)env->GetIntField(jAudioDevicePort, gAudioPortFields.mType); - jstring jDeviceAddress = (jstring)env->GetObjectField(jAudioDevicePort, - gAudioPortFields.mAddress); + nAudioPortConfig->ext.device.type = static_cast<audio_devices_t>( + env->GetIntField(jAudioDevicePort, gAudioPortFields.mType)); + jstring jDeviceAddress = + static_cast<jstring>(env->GetObjectField(jAudioDevicePort, gAudioPortFields.mAddress)); const char *nDeviceAddress = env->GetStringUTFChars(jDeviceAddress, NULL); strncpy(nAudioPortConfig->ext.device.address, nDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN - 1); @@ -1043,45 +1046,41 @@ static jint convertAudioPortConfigToNativeWithDevicePort(JNIEnv *env, return jStatus; } -static jint convertAudioPortConfigFromNative(JNIEnv *env, - jobject jAudioPort, - jobject *jAudioPortConfig, - const struct audio_port_config *nAudioPortConfig) -{ - jint jStatus = AUDIO_JAVA_SUCCESS; - jobject jAudioGainConfig = NULL; - jobject jAudioGain = NULL; +static jint convertAudioPortConfigFromNative(JNIEnv *env, ScopedLocalRef<jobject> *jAudioPort, + ScopedLocalRef<jobject> *jAudioPortConfig, + const struct audio_port_config *nAudioPortConfig) { jintArray jGainValues; bool audioportCreated = false; ALOGV("convertAudioPortConfigFromNative jAudioPort %p", jAudioPort); - if (jAudioPort == NULL) { - jobject jHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor, - nAudioPortConfig->id); + if (*jAudioPort == nullptr) { + ScopedLocalRef<jobject> jHandle(env, + env->NewObject(gAudioHandleClass, gAudioHandleCstor, + nAudioPortConfig->id)); ALOGV("convertAudioPortConfigFromNative handle %d is a %s", nAudioPortConfig->id, nAudioPortConfig->type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix"); if (jHandle == NULL) { - return (jint)AUDIO_JAVA_ERROR; + return AUDIO_JAVA_ERROR; } // create placeholder port and port config objects with just the correct handle // and configuration data. The actual AudioPortConfig objects will be // constructed by java code with correct class type (device, mix etc...) // and reference to AudioPort instance in this client - jAudioPort = env->NewObject(gAudioPortClass, gAudioPortCstor, - jHandle, // handle - 0, // role - NULL, // name - NULL, // samplingRates - NULL, // channelMasks - NULL, // channelIndexMasks - NULL, // formats - NULL); // gains - env->DeleteLocalRef(jHandle); - if (jAudioPort == NULL) { - return (jint)AUDIO_JAVA_ERROR; + jAudioPort->reset(env->NewObject(gAudioPortClass, gAudioPortCstor, + jHandle.get(), // handle + 0, // role + NULL, // name + NULL, // samplingRates + NULL, // channelMasks + NULL, // channelIndexMasks + NULL, // formats + NULL)); // gains + + if (*jAudioPort == nullptr) { + return AUDIO_JAVA_ERROR; } ALOGV("convertAudioPortConfigFromNative jAudioPort created for handle %d", nAudioPortConfig->id); @@ -1089,6 +1088,9 @@ static jint convertAudioPortConfigFromNative(JNIEnv *env, audioportCreated = true; } + ScopedLocalRef<jobject> jAudioGainConfig(env, nullptr); + ScopedLocalRef<jobject> jAudioGain(env, nullptr); + bool useInMask = audio_port_config_has_input_direction(nAudioPortConfig); audio_channel_mask_t nMask; @@ -1102,36 +1104,26 @@ static jint convertAudioPortConfigFromNative(JNIEnv *env, gainIndex, nAudioPortConfig->gain.mode); if (audioportCreated) { ALOGV("convertAudioPortConfigFromNative creating gain"); - jAudioGain = env->NewObject(gAudioGainClass, gAudioGainCstor, - gainIndex, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0); + jAudioGain.reset(env->NewObject(gAudioGainClass, gAudioGainCstor, gainIndex, 0, 0, 0, 0, + 0, 0, 0, 0)); if (jAudioGain == NULL) { ALOGV("convertAudioPortConfigFromNative creating gain FAILED"); - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } } else { ALOGV("convertAudioPortConfigFromNative reading gain from port"); - jobjectArray jGains = (jobjectArray)env->GetObjectField(jAudioPort, - gAudioPortFields.mGains); + ScopedLocalRef<jobjectArray> + jGains(env, + static_cast<jobjectArray>(env->GetObjectField(jAudioPort->get(), + gAudioPortFields.mGains))); if (jGains == NULL) { ALOGV("convertAudioPortConfigFromNative could not get gains from port"); - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } - jAudioGain = env->GetObjectArrayElement(jGains, gainIndex); - env->DeleteLocalRef(jGains); + jAudioGain.reset(env->GetObjectArrayElement(jGains.get(), gainIndex)); if (jAudioGain == NULL) { ALOGV("convertAudioPortConfigFromNative could not get gain at index %d", gainIndex); - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } } int numValues; @@ -1143,8 +1135,7 @@ static jint convertAudioPortConfigFromNative(JNIEnv *env, jGainValues = env->NewIntArray(numValues); if (jGainValues == NULL) { ALOGV("convertAudioPortConfigFromNative could not create gain values %d", numValues); - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } env->SetIntArrayRegion(jGainValues, 0, numValues, nAudioPortConfig->gain.values); @@ -1158,19 +1149,14 @@ static jint convertAudioPortConfigFromNative(JNIEnv *env, ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask); } - jAudioGainConfig = env->NewObject(gAudioGainConfigClass, - gAudioGainConfigCstor, - gainIndex, - jAudioGain, - nAudioPortConfig->gain.mode, - jMask, - jGainValues, - nAudioPortConfig->gain.ramp_duration_ms); + jAudioGainConfig.reset(env->NewObject(gAudioGainConfigClass, gAudioGainConfigCstor, + gainIndex, jAudioGain.get(), + nAudioPortConfig->gain.mode, jMask, jGainValues, + nAudioPortConfig->gain.ramp_duration_ms)); env->DeleteLocalRef(jGainValues); if (jAudioGainConfig == NULL) { ALOGV("convertAudioPortConfigFromNative could not create gain config"); - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } } jclass clazz; @@ -1180,17 +1166,16 @@ static jint convertAudioPortConfigFromNative(JNIEnv *env, methodID = gAudioPortConfigCstor; ALOGV("convertAudioPortConfigFromNative building a generic port config"); } else { - if (env->IsInstanceOf(jAudioPort, gAudioDevicePortClass)) { + if (env->IsInstanceOf(jAudioPort->get(), gAudioDevicePortClass)) { clazz = gAudioDevicePortConfigClass; methodID = gAudioDevicePortConfigCstor; ALOGV("convertAudioPortConfigFromNative building a device config"); - } else if (env->IsInstanceOf(jAudioPort, gAudioMixPortClass)) { + } else if (env->IsInstanceOf(jAudioPort->get(), gAudioMixPortClass)) { clazz = gAudioMixPortConfigClass; methodID = gAudioMixPortConfigCstor; ALOGV("convertAudioPortConfigFromNative building a mix config"); } else { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } } nMask = (nAudioPortConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) @@ -1204,8 +1189,8 @@ static jint convertAudioPortConfigFromNative(JNIEnv *env, ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask); } - *jAudioPortConfig = - env->NewObject(clazz, methodID, jAudioPort, + jAudioPortConfig->reset( + env->NewObject(clazz, methodID, jAudioPort->get(), (nAudioPortConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) ? nAudioPortConfig->sample_rate : AUDIO_CONFIG_BASE_INITIALIZER.sample_rate, @@ -1214,31 +1199,14 @@ static jint convertAudioPortConfigFromNative(JNIEnv *env, (nAudioPortConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT) ? nAudioPortConfig->format : AUDIO_CONFIG_BASE_INITIALIZER.format), - jAudioGainConfig); + jAudioGainConfig.get())); if (*jAudioPortConfig == NULL) { ALOGV("convertAudioPortConfigFromNative could not create new port config"); - jStatus = (jint)AUDIO_JAVA_ERROR; + return AUDIO_JAVA_ERROR; } else { ALOGV("convertAudioPortConfigFromNative OK"); } - -exit: - if (audioportCreated) { - env->DeleteLocalRef(jAudioPort); - if (jAudioGain != NULL) { - env->DeleteLocalRef(jAudioGain); - } - } - if (jAudioGainConfig != NULL) { - env->DeleteLocalRef(jAudioGainConfig); - } - return jStatus; -} - -// TODO: pull out to separate file -template <typename T, size_t N> -static constexpr size_t array_size(const T (&)[N]) { - return N; + return AUDIO_JAVA_SUCCESS; } static jintArray convertEncapsulationInfoFromNative(JNIEnv *env, uint32_t encapsulationInfo) { @@ -1252,7 +1220,8 @@ static jintArray convertEncapsulationInfoFromNative(JNIEnv *env, uint32_t encaps } } jintArray result = env->NewIntArray(encapsulation.size()); - env->SetIntArrayRegion(result, 0, encapsulation.size(), (jint *)encapsulation.data()); + env->SetIntArrayRegion(result, 0, encapsulation.size(), + reinterpret_cast<jint *>(encapsulation.data())); return result; } @@ -1260,8 +1229,8 @@ static bool isAudioPortArrayCountOutOfBounds(const struct audio_port_v7 *nAudioP std::stringstream &ss) { ss << " num_audio_profiles " << nAudioPort->num_audio_profiles << " num_gains " << nAudioPort->num_gains; - if (nAudioPort->num_audio_profiles > array_size(nAudioPort->audio_profiles) || - nAudioPort->num_gains > array_size(nAudioPort->gains)) { + if (nAudioPort->num_audio_profiles > std::size(nAudioPort->audio_profiles) || + nAudioPort->num_gains > std::size(nAudioPort->gains)) { return true; } for (size_t i = 0; i < nAudioPort->num_audio_profiles; ++i) { @@ -1269,9 +1238,9 @@ static bool isAudioPortArrayCountOutOfBounds(const struct audio_port_v7 *nAudioP << " num_sample_rates " << nAudioPort->audio_profiles[i].num_sample_rates << " num_channel_masks " << nAudioPort->audio_profiles[i].num_channel_masks; if (nAudioPort->audio_profiles[i].num_sample_rates > - array_size(nAudioPort->audio_profiles[i].sample_rates) || + std::size(nAudioPort->audio_profiles[i].sample_rates) || nAudioPort->audio_profiles[i].num_channel_masks > - array_size(nAudioPort->audio_profiles[i].channel_masks)) { + std::size(nAudioPort->audio_profiles[i].channel_masks)) { return true; } } @@ -1309,7 +1278,8 @@ static jint convertAudioProfileFromNative(JNIEnv *env, jobject *jAudioProfile, if (nAudioProfile->num_sample_rates) { env->SetIntArrayRegion(jSamplingRates.get(), 0 /*start*/, nAudioProfile->num_sample_rates, - (jint *)nAudioProfile->sample_rates); + const_cast<jint *>(reinterpret_cast<const jint *>( + nAudioProfile->sample_rates))); } // put the masks in the output arrays @@ -1342,18 +1312,8 @@ static jint convertAudioProfileFromNative(JNIEnv *env, jobject *jAudioProfile, return AUDIO_JAVA_SUCCESS; } -static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, +static jint convertAudioPortFromNative(JNIEnv *env, ScopedLocalRef<jobject> *jAudioPort, const struct audio_port_v7 *nAudioPort) { - jint jStatus = (jint)AUDIO_JAVA_SUCCESS; - jintArray jEncapsulationModes = NULL; - jintArray jEncapsulationMetadataTypes = NULL; - jobjectArray jGains = NULL; - jobject jHandle = NULL; - jobject jAudioPortConfig = NULL; - jstring jDeviceName = NULL; - jobject jAudioProfiles = NULL; - jobject jAudioDescriptors = nullptr; - ScopedLocalRef<jobject> jPcmFloatProfileFromExtendedInteger(env, nullptr); bool hasFloat = false; bool useInMask; @@ -1377,19 +1337,21 @@ static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, } else { ALOGE("%s", s.c_str()); } - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } useInMask = audio_has_input_direction(nAudioPort->type, nAudioPort->role); - jAudioProfiles = env->NewObject(gArrayListClass, gArrayListMethods.cstor); + ScopedLocalRef<jobject> jAudioProfiles(env, + env->NewObject(gArrayListClass, + gArrayListMethods.cstor)); if (jAudioProfiles == nullptr) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } + ScopedLocalRef<jobject> jPcmFloatProfileFromExtendedInteger(env, nullptr); for (size_t i = 0; i < nAudioPort->num_audio_profiles; ++i) { jobject jAudioProfile = nullptr; + jint jStatus = AUDIO_JAVA_SUCCESS; jStatus = convertAudioProfileFromNative(env, &jAudioProfile, &nAudioPort->audio_profiles[i], useInMask); if (jStatus == AUDIO_JAVA_BAD_VALUE) { @@ -1397,10 +1359,9 @@ static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, continue; } if (jStatus != NO_ERROR) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } - env->CallBooleanMethod(jAudioProfiles, gArrayListMethods.add, jAudioProfile); + env->CallBooleanMethod(jAudioProfiles.get(), gArrayListMethods.add, jAudioProfile); if (nAudioPort->audio_profiles[i].format == AUDIO_FORMAT_PCM_FLOAT) { hasFloat = true; @@ -1409,19 +1370,21 @@ static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, audio_bytes_per_sample(nAudioPort->audio_profiles[i].format) > 2) { ScopedLocalRef<jintArray> jSamplingRates(env, - (jintArray) + static_cast<jintArray>( env->GetObjectField(jAudioProfile, - gAudioProfileFields.mSamplingRates)); + gAudioProfileFields + .mSamplingRates))); ScopedLocalRef<jintArray> jChannelMasks(env, - (jintArray) + static_cast<jintArray>( env->GetObjectField(jAudioProfile, - gAudioProfileFields.mChannelMasks)); + gAudioProfileFields.mChannelMasks))); ScopedLocalRef<jintArray> jChannelIndexMasks(env, - (jintArray)env->GetObjectField(jAudioProfile, - gAudioProfileFields - .mChannelIndexMasks)); + static_cast<jintArray>( + env->GetObjectField(jAudioProfile, + gAudioProfileFields + .mChannelIndexMasks))); int encapsulationType = env->GetIntField(jAudioProfile, gAudioProfileFields.mEncapsulationType); @@ -1441,14 +1404,15 @@ static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, // (replacing the zero pad). This ensures pre-S apps that look // for ENCODING_PCM_FLOAT continue to see that encoding if the device supports // extended precision integers. - env->CallBooleanMethod(jAudioProfiles, gArrayListMethods.add, + env->CallBooleanMethod(jAudioProfiles.get(), gArrayListMethods.add, jPcmFloatProfileFromExtendedInteger.get()); } - jAudioDescriptors = env->NewObject(gArrayListClass, gArrayListMethods.cstor); + ScopedLocalRef<jobject> jAudioDescriptors(env, + env->NewObject(gArrayListClass, + gArrayListMethods.cstor)); if (jAudioDescriptors == nullptr) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } for (size_t i = 0; i < nAudioPort->num_extra_audio_descriptors; ++i) { const auto &extraAudioDescriptor = nAudioPort->extra_audio_descriptors[i]; @@ -1478,15 +1442,16 @@ static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, env->NewObject(gAudioDescriptorClass, gAudioDescriptorCstor, standard, encapsulationType, jDescriptor.get())); - env->CallBooleanMethod(jAudioDescriptors, gArrayListMethods.add, jAudioDescriptor.get()); + env->CallBooleanMethod(jAudioDescriptors.get(), gArrayListMethods.add, + jAudioDescriptor.get()); } // gains - jGains = env->NewObjectArray(nAudioPort->num_gains, - gAudioGainClass, NULL); - if (jGains == NULL) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + ScopedLocalRef<jobjectArray> jGains(env, + env->NewObjectArray(nAudioPort->num_gains, gAudioGainClass, + NULL)); + if (jGains == nullptr) { + return AUDIO_JAVA_ERROR; } for (size_t j = 0; j < nAudioPort->num_gains; j++) { @@ -1511,88 +1476,71 @@ static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, nAudioPort->gains[j].min_ramp_ms, nAudioPort->gains[j].max_ramp_ms); if (jGain == NULL) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } - env->SetObjectArrayElement(jGains, j, jGain); + env->SetObjectArrayElement(jGains.get(), j, jGain); env->DeleteLocalRef(jGain); } - jHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor, - nAudioPort->id); - if (jHandle == NULL) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + ScopedLocalRef<jobject> jHandle(env, + env->NewObject(gAudioHandleClass, gAudioHandleCstor, + nAudioPort->id)); + if (jHandle == nullptr) { + return AUDIO_JAVA_ERROR; } - jDeviceName = env->NewStringUTF(nAudioPort->name); - + ScopedLocalRef<jstring> jDeviceName(env, env->NewStringUTF(nAudioPort->name)); if (nAudioPort->type == AUDIO_PORT_TYPE_DEVICE) { - ALOGV("convertAudioPortFromNative is a device %08x", nAudioPort->ext.device.type); - jstring jAddress = env->NewStringUTF(nAudioPort->ext.device.address); - jEncapsulationModes = - convertEncapsulationInfoFromNative(env, nAudioPort->ext.device.encapsulation_modes); - jEncapsulationMetadataTypes = + ScopedLocalRef<jintArray> jEncapsulationModes( + env, + convertEncapsulationInfoFromNative(env, + nAudioPort->ext.device.encapsulation_modes)); + ScopedLocalRef<jintArray> jEncapsulationMetadataTypes( + env, convertEncapsulationInfoFromNative(env, nAudioPort->ext.device - .encapsulation_metadata_types); - *jAudioPort = - env->NewObject(gAudioDevicePortClass, gAudioDevicePortCstor, jHandle, jDeviceName, - jAudioProfiles, jGains, nAudioPort->ext.device.type, jAddress, - jEncapsulationModes, jEncapsulationMetadataTypes, jAudioDescriptors); + .encapsulation_metadata_types)); + ALOGV("convertAudioPortFromNative is a device %08x", nAudioPort->ext.device.type); + jstring jAddress = env->NewStringUTF(nAudioPort->ext.device.address); + jAudioPort->reset( + env->NewObject(gAudioDevicePortClass, gAudioDevicePortCstor, jHandle.get(), + jDeviceName.get(), jAudioProfiles.get(), jGains.get(), + nAudioPort->ext.device.type, jAddress, jEncapsulationModes.get(), + jEncapsulationMetadataTypes.get(), jAudioDescriptors.get())); env->DeleteLocalRef(jAddress); } else if (nAudioPort->type == AUDIO_PORT_TYPE_MIX) { ALOGV("convertAudioPortFromNative is a mix"); - *jAudioPort = env->NewObject(gAudioMixPortClass, gAudioMixPortCstor, jHandle, - nAudioPort->ext.mix.handle, nAudioPort->role, jDeviceName, - jAudioProfiles, jGains); + jAudioPort->reset(env->NewObject(gAudioMixPortClass, gAudioMixPortCstor, jHandle.get(), + nAudioPort->ext.mix.handle, nAudioPort->role, + jDeviceName.get(), jAudioProfiles.get(), jGains.get())); } else { ALOGE("convertAudioPortFromNative unknown nAudioPort type %d", nAudioPort->type); - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } if (*jAudioPort == NULL) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } - jStatus = convertAudioPortConfigFromNative(env, - *jAudioPort, - &jAudioPortConfig, + ScopedLocalRef<jobject> jAudioPortConfig(env, nullptr); + + if (int jStatus = convertAudioPortConfigFromNative(env, jAudioPort, &jAudioPortConfig, &nAudioPort->active_config); - if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + jStatus != AUDIO_JAVA_SUCCESS) { + return jStatus; } - env->SetObjectField(*jAudioPort, gAudioPortFields.mActiveConfig, jAudioPortConfig); + env->SetObjectField(jAudioPort->get(), gAudioPortFields.mActiveConfig, jAudioPortConfig.get()); + return AUDIO_JAVA_SUCCESS; +} -exit: - if (jDeviceName != NULL) { - env->DeleteLocalRef(jDeviceName); - } - if (jEncapsulationModes != NULL) { - env->DeleteLocalRef(jEncapsulationModes); - } - if (jEncapsulationMetadataTypes != NULL) { - env->DeleteLocalRef(jEncapsulationMetadataTypes); - } - if (jAudioProfiles != NULL) { - env->DeleteLocalRef(jAudioProfiles); - } - if (jGains != NULL) { - env->DeleteLocalRef(jGains); - } - if (jHandle != NULL) { - env->DeleteLocalRef(jHandle); - } - if (jAudioPortConfig != NULL) { - env->DeleteLocalRef(jAudioPortConfig); - } - if (jAudioDescriptors != nullptr) { - env->DeleteLocalRef(jAudioDescriptors); +static bool setGeneration(JNIEnv *env, jintArray jGeneration, unsigned int generation1) { + ScopedIntArrayRW nGeneration(env, jGeneration); + if (nGeneration.get() == nullptr) { + return false; + } else { + nGeneration[0] = generation1; + return true; } - - return jStatus; } static jint @@ -1603,23 +1551,22 @@ android_media_AudioSystem_listAudioPorts(JNIEnv *env, jobject clazz, if (jPorts == NULL) { ALOGE("listAudioPorts NULL AudioPort ArrayList"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (!env->IsInstanceOf(jPorts, gArrayListClass)) { ALOGE("listAudioPorts not an arraylist"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (jGeneration == NULL || env->GetArrayLength(jGeneration) != 1) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } status_t status; unsigned int generation1; unsigned int generation; unsigned int numPorts; - jint *nGeneration; - struct audio_port_v7 *nPorts = nullptr; + std::vector<audio_port_v7> nPorts; int attempts = MAX_PORT_GENERATION_SYNC_ATTEMPTS; jint jStatus; @@ -1638,43 +1585,39 @@ android_media_AudioSystem_listAudioPorts(JNIEnv *env, jobject clazz, break; } if (numPorts == 0) { - jStatus = (jint)AUDIO_JAVA_SUCCESS; - goto exit; + return setGeneration(env, jGeneration, generation1) ? AUDIO_JAVA_SUCCESS + : AUDIO_JAVA_ERROR; } - nPorts = (struct audio_port_v7 *)realloc(nPorts, numPorts * sizeof(struct audio_port_v7)); + nPorts.resize(numPorts); status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE, AUDIO_PORT_TYPE_NONE, &numPorts, - nPorts, &generation); + &nPorts[0], &generation); ALOGV("listAudioPorts AudioSystem::listAudioPorts numPorts %d generation %d generation1 %d", numPorts, generation, generation1); } while (generation1 != generation && status == NO_ERROR); jStatus = nativeToJavaStatus(status); if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + if (!setGeneration(env, jGeneration, generation1)) { + jStatus = AUDIO_JAVA_ERROR; + } + return jStatus; } for (size_t i = 0; i < numPorts; i++) { - jobject jAudioPort = NULL; + ScopedLocalRef<jobject> jAudioPort(env, nullptr); jStatus = convertAudioPortFromNative(env, &jAudioPort, &nPorts[i]); if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; - } - env->CallBooleanMethod(jPorts, gArrayListMethods.add, jAudioPort); - if (jAudioPort != NULL) { - env->DeleteLocalRef(jAudioPort); + if (!setGeneration(env, jGeneration, generation1)) { + jStatus = AUDIO_JAVA_ERROR; + } + return jStatus; } + env->CallBooleanMethod(jPorts, gArrayListMethods.add, jAudioPort.get()); } - -exit: - nGeneration = env->GetIntArrayElements(jGeneration, NULL); - if (nGeneration == NULL) { - jStatus = (jint)AUDIO_JAVA_ERROR; - } else { - nGeneration[0] = generation1; - env->ReleaseIntArrayElements(jGeneration, nGeneration, 0); + if (!setGeneration(env, jGeneration, generation1)) { + jStatus = AUDIO_JAVA_ERROR; } - free(nPorts); return jStatus; } @@ -1687,64 +1630,56 @@ android_media_AudioSystem_createAudioPatch(JNIEnv *env, jobject clazz, ALOGV("createAudioPatch"); if (jPatches == NULL || jSources == NULL || jSinks == NULL) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (env->GetArrayLength(jPatches) != 1) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } jint numSources = env->GetArrayLength(jSources); if (numSources == 0 || numSources > AUDIO_PATCH_PORTS_MAX) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } jint numSinks = env->GetArrayLength(jSinks); if (numSinks == 0 || numSinks > AUDIO_PATCH_PORTS_MAX) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } - audio_patch_handle_t handle = (audio_patch_handle_t)0; - jobject jPatch = env->GetObjectArrayElement(jPatches, 0); - jobject jPatchHandle = NULL; + audio_patch_handle_t handle = static_cast<audio_patch_handle_t>(AUDIO_PATCH_HANDLE_NONE); + ScopedLocalRef<jobject> jPatch(env, env->GetObjectArrayElement(jPatches, 0)); + ScopedLocalRef<jobject> jPatchHandle(env, nullptr); if (jPatch != NULL) { - if (!env->IsInstanceOf(jPatch, gAudioPatchClass)) { - return (jint)AUDIO_JAVA_BAD_VALUE; + if (!env->IsInstanceOf(jPatch.get(), gAudioPatchClass)) { + return AUDIO_JAVA_BAD_VALUE; } - jPatchHandle = env->GetObjectField(jPatch, gAudioPatchFields.mHandle); - handle = (audio_patch_handle_t)env->GetIntField(jPatchHandle, gAudioHandleFields.mId); + jPatchHandle.reset(env->GetObjectField(jPatch.get(), gAudioPatchFields.mHandle)); + handle = static_cast<audio_patch_handle_t>( + env->GetIntField(jPatchHandle.get(), gAudioHandleFields.mId)); } struct audio_patch nPatch = { .id = handle }; - jobject jSource = NULL; - jobject jSink = NULL; - for (jint i = 0; i < numSources; i++) { - jSource = env->GetObjectArrayElement(jSources, i); - if (!env->IsInstanceOf(jSource, gAudioPortConfigClass)) { - jStatus = (jint)AUDIO_JAVA_BAD_VALUE; - goto exit; - } - jStatus = convertAudioPortConfigToNative(env, &nPatch.sources[i], jSource, false); - env->DeleteLocalRef(jSource); - jSource = NULL; + ScopedLocalRef<jobject> jSource(env, env->GetObjectArrayElement(jSources, i)); + if (!env->IsInstanceOf(jSource.get(), gAudioPortConfigClass)) { + return AUDIO_JAVA_BAD_VALUE; + } + jStatus = convertAudioPortConfigToNative(env, &nPatch.sources[i], jSource.get(), false); if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + return jStatus; } nPatch.num_sources++; } for (jint i = 0; i < numSinks; i++) { - jSink = env->GetObjectArrayElement(jSinks, i); - if (!env->IsInstanceOf(jSink, gAudioPortConfigClass)) { - jStatus = (jint)AUDIO_JAVA_BAD_VALUE; - goto exit; - } - jStatus = convertAudioPortConfigToNative(env, &nPatch.sinks[i], jSink, false); - env->DeleteLocalRef(jSink); - jSink = NULL; + ScopedLocalRef<jobject> jSink(env, env->GetObjectArrayElement(jSinks, i)); + if (!env->IsInstanceOf(jSink.get(), gAudioPortConfigClass)) { + return AUDIO_JAVA_BAD_VALUE; + } + jStatus = convertAudioPortConfigToNative(env, &nPatch.sinks[i], jSink.get(), false); if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + return jStatus; } nPatch.num_sinks++; } @@ -1755,38 +1690,22 @@ android_media_AudioSystem_createAudioPatch(JNIEnv *env, jobject clazz, jStatus = nativeToJavaStatus(status); if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + return jStatus; } - if (jPatchHandle == NULL) { - jPatchHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor, - handle); - if (jPatchHandle == NULL) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + if (jPatchHandle == nullptr) { + jPatchHandle.reset(env->NewObject(gAudioHandleClass, gAudioHandleCstor, handle)); + if (jPatchHandle == nullptr) { + return AUDIO_JAVA_ERROR; } - jPatch = env->NewObject(gAudioPatchClass, gAudioPatchCstor, jPatchHandle, jSources, jSinks); - if (jPatch == NULL) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + jPatch.reset(env->NewObject(gAudioPatchClass, gAudioPatchCstor, jPatchHandle.get(), + jSources, jSinks)); + if (jPatch == nullptr) { + return AUDIO_JAVA_ERROR; } - env->SetObjectArrayElement(jPatches, 0, jPatch); + env->SetObjectArrayElement(jPatches, 0, jPatch.get()); } else { - env->SetIntField(jPatchHandle, gAudioHandleFields.mId, handle); - } - -exit: - if (jPatchHandle != NULL) { - env->DeleteLocalRef(jPatchHandle); - } - if (jPatch != NULL) { - env->DeleteLocalRef(jPatch); - } - if (jSource != NULL) { - env->DeleteLocalRef(jSource); - } - if (jSink != NULL) { - env->DeleteLocalRef(jSink); + env->SetIntField(jPatchHandle.get(), gAudioHandleFields.mId, handle); } return jStatus; } @@ -1797,16 +1716,17 @@ android_media_AudioSystem_releaseAudioPatch(JNIEnv *env, jobject clazz, { ALOGV("releaseAudioPatch"); if (jPatch == NULL) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } - audio_patch_handle_t handle = (audio_patch_handle_t)0; + audio_patch_handle_t handle = static_cast<audio_patch_handle_t>(AUDIO_PATCH_HANDLE_NONE); jobject jPatchHandle = NULL; if (!env->IsInstanceOf(jPatch, gAudioPatchClass)) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } jPatchHandle = env->GetObjectField(jPatch, gAudioPatchFields.mHandle); - handle = (audio_patch_handle_t)env->GetIntField(jPatchHandle, gAudioHandleFields.mId); + handle = static_cast<audio_patch_handle_t>( + env->GetIntField(jPatchHandle, gAudioHandleFields.mId)); env->DeleteLocalRef(jPatchHandle); ALOGV("AudioSystem::releaseAudioPatch"); @@ -1823,28 +1743,22 @@ android_media_AudioSystem_listAudioPatches(JNIEnv *env, jobject clazz, ALOGV("listAudioPatches"); if (jPatches == NULL) { ALOGE("listAudioPatches NULL AudioPatch ArrayList"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (!env->IsInstanceOf(jPatches, gArrayListClass)) { ALOGE("listAudioPatches not an arraylist"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (jGeneration == NULL || env->GetArrayLength(jGeneration) != 1) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } status_t status; unsigned int generation1; unsigned int generation; unsigned int numPatches; - jint *nGeneration; - struct audio_patch *nPatches = NULL; - jobjectArray jSources = NULL; - jobject jSource = NULL; - jobjectArray jSinks = NULL; - jobject jSink = NULL; - jobject jPatch = NULL; + std::vector<audio_patch> nPatches; int attempts = MAX_PORT_GENERATION_SYNC_ATTEMPTS; jint jStatus; @@ -1865,15 +1779,13 @@ android_media_AudioSystem_listAudioPatches(JNIEnv *env, jobject clazz, break; } if (numPatches == 0) { - jStatus = (jint)AUDIO_JAVA_SUCCESS; - goto exit; + return setGeneration(env, jGeneration, generation1) ? AUDIO_JAVA_SUCCESS + : AUDIO_JAVA_ERROR; } - nPatches = (struct audio_patch *)realloc(nPatches, numPatches * sizeof(struct audio_patch)); + nPatches.resize(numPatches); - status = AudioSystem::listAudioPatches(&numPatches, - nPatches, - &generation); + status = AudioSystem::listAudioPatches(&numPatches, &nPatches[0], &generation); ALOGV("listAudioPatches AudioSystem::listAudioPatches numPatches %d generation %d generation1 %d", numPatches, generation, generation1); @@ -1881,15 +1793,21 @@ android_media_AudioSystem_listAudioPatches(JNIEnv *env, jobject clazz, jStatus = nativeToJavaStatus(status); if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + if (!setGeneration(env, jGeneration, generation1)) { + jStatus = AUDIO_JAVA_ERROR; + } + return jStatus; } for (size_t i = 0; i < numPatches; i++) { + ScopedLocalRef<jobject> jPatch(env, nullptr); + ScopedLocalRef<jobjectArray> jSources(env, nullptr); + ScopedLocalRef<jobjectArray> jSinks(env, nullptr); jobject patchHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor, nPatches[i].id); if (patchHandle == NULL) { - jStatus = AUDIO_JAVA_ERROR; - goto exit; + setGeneration(env, jGeneration, generation1); + return AUDIO_JAVA_ERROR; } ALOGV("listAudioPatches patch %zu num_sources %d num_sinks %d", i, nPatches[i].num_sources, nPatches[i].num_sinks); @@ -1897,96 +1815,67 @@ android_media_AudioSystem_listAudioPatches(JNIEnv *env, jobject clazz, env->SetIntField(patchHandle, gAudioHandleFields.mId, nPatches[i].id); // load sources - jSources = env->NewObjectArray(nPatches[i].num_sources, - gAudioPortConfigClass, NULL); - if (jSources == NULL) { - jStatus = AUDIO_JAVA_ERROR; - goto exit; + jSources.reset(env->NewObjectArray(nPatches[i].num_sources, gAudioPortConfigClass, NULL)); + if (jSources == nullptr) { + setGeneration(env, jGeneration, generation1); + return AUDIO_JAVA_ERROR; } for (size_t j = 0; j < nPatches[i].num_sources; j++) { - jStatus = convertAudioPortConfigFromNative(env, - NULL, - &jSource, - &nPatches[i].sources[j]); + ScopedLocalRef<jobject> jSource(env, nullptr); + ScopedLocalRef<jobject> jAudioPort(env, nullptr); + jStatus = convertAudioPortConfigFromNative(env, &jAudioPort, &jSource, + &nPatches[i].sources[j]); if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + if (!setGeneration(env, jGeneration, generation1)) { + jStatus = AUDIO_JAVA_ERROR; + } + return jStatus; } - env->SetObjectArrayElement(jSources, j, jSource); - env->DeleteLocalRef(jSource); - jSource = NULL; + env->SetObjectArrayElement(jSources.get(), j, jSource.get()); ALOGV("listAudioPatches patch %zu source %zu is a %s handle %d", i, j, nPatches[i].sources[j].type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix", nPatches[i].sources[j].id); } // load sinks - jSinks = env->NewObjectArray(nPatches[i].num_sinks, - gAudioPortConfigClass, NULL); - if (jSinks == NULL) { - jStatus = AUDIO_JAVA_ERROR; - goto exit; + jSinks.reset(env->NewObjectArray(nPatches[i].num_sinks, gAudioPortConfigClass, NULL)); + if (jSinks == nullptr) { + setGeneration(env, jGeneration, generation1); + return AUDIO_JAVA_ERROR; } for (size_t j = 0; j < nPatches[i].num_sinks; j++) { - jStatus = convertAudioPortConfigFromNative(env, - NULL, - &jSink, - &nPatches[i].sinks[j]); + ScopedLocalRef<jobject> jSink(env, nullptr); + ScopedLocalRef<jobject> jAudioPort(env, nullptr); + jStatus = convertAudioPortConfigFromNative(env, &jAudioPort, &jSink, + &nPatches[i].sinks[j]); if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + if (!setGeneration(env, jGeneration, generation1)) { + jStatus = AUDIO_JAVA_ERROR; + } + return jStatus; } - env->SetObjectArrayElement(jSinks, j, jSink); - env->DeleteLocalRef(jSink); - jSink = NULL; + env->SetObjectArrayElement(jSinks.get(), j, jSink.get()); ALOGV("listAudioPatches patch %zu sink %zu is a %s handle %d", i, j, nPatches[i].sinks[j].type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix", nPatches[i].sinks[j].id); } - jPatch = env->NewObject(gAudioPatchClass, gAudioPatchCstor, - patchHandle, jSources, jSinks); - env->DeleteLocalRef(jSources); - jSources = NULL; - env->DeleteLocalRef(jSinks); - jSinks = NULL; - if (jPatch == NULL) { + jPatch.reset(env->NewObject(gAudioPatchClass, gAudioPatchCstor, patchHandle, jSources.get(), + jSinks.get())); + if (jPatch == nullptr) { jStatus = AUDIO_JAVA_ERROR; - goto exit; + setGeneration(env, jGeneration, generation1); + return AUDIO_JAVA_ERROR; } - env->CallBooleanMethod(jPatches, gArrayListMethods.add, jPatch); - env->DeleteLocalRef(jPatch); - jPatch = NULL; + env->CallBooleanMethod(jPatches, gArrayListMethods.add, jPatch.get()); } - -exit: - - nGeneration = env->GetIntArrayElements(jGeneration, NULL); - if (nGeneration == NULL) { + if (!setGeneration(env, jGeneration, generation1)) { jStatus = AUDIO_JAVA_ERROR; - } else { - nGeneration[0] = generation1; - env->ReleaseIntArrayElements(jGeneration, nGeneration, 0); - } - - if (jSources != NULL) { - env->DeleteLocalRef(jSources); } - if (jSource != NULL) { - env->DeleteLocalRef(jSource); - } - if (jSinks != NULL) { - env->DeleteLocalRef(jSinks); - } - if (jSink != NULL) { - env->DeleteLocalRef(jSink); - } - if (jPatch != NULL) { - env->DeleteLocalRef(jPatch); - } - free(nPatches); return jStatus; } @@ -2035,7 +1924,7 @@ android_media_AudioSystem_startAudioSource(JNIEnv *env, jobject clazz, } auto paa = JNIAudioAttributeHelper::makeUnique(); jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jAudioAttributes, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return jStatus; } audio_port_handle_t handle; @@ -2052,8 +1941,7 @@ static jint android_media_AudioSystem_stopAudioSource(JNIEnv *env, jobject clazz, jint handle) { ALOGV("stopAudioSource"); - status_t status = AudioSystem::stopAudioSource( - static_cast <audio_port_handle_t>(handle)); + status_t status = AudioSystem::stopAudioSource(static_cast<audio_port_handle_t>(handle)); ALOGV("AudioSystem::stopAudioSource() returned %d", status); return nativeToJavaStatus(status); } @@ -2085,7 +1973,7 @@ android_media_AudioSystem_eventHandlerFinalize(JNIEnv *env, jobject thiz) static jint android_media_AudioSystem_getAudioHwSyncForSession(JNIEnv *env, jobject thiz, jint sessionId) { - return (jint) AudioSystem::getAudioHwSyncForSession((audio_session_t) sessionId); + return AudioSystem::getAudioHwSyncForSession(static_cast<audio_session_t>(sessionId)); } static void @@ -2204,11 +2092,11 @@ static jint convertAudioMixToNative(JNIEnv *env, { nAudioMix->mMixType = env->GetIntField(jAudioMix, gAudioMixFields.mMixType); nAudioMix->mRouteFlags = env->GetIntField(jAudioMix, gAudioMixFields.mRouteFlags); - nAudioMix->mDeviceType = (audio_devices_t) - env->GetIntField(jAudioMix, gAudioMixFields.mDeviceType); + nAudioMix->mDeviceType = + static_cast<audio_devices_t>(env->GetIntField(jAudioMix, gAudioMixFields.mDeviceType)); - jstring jDeviceAddress = (jstring)env->GetObjectField(jAudioMix, - gAudioMixFields.mDeviceAddress); + jstring jDeviceAddress = + static_cast<jstring>(env->GetObjectField(jAudioMix, gAudioMixFields.mDeviceAddress)); const char *nDeviceAddress = env->GetStringUTFChars(jDeviceAddress, NULL); nAudioMix->mDeviceAddress = String8(nDeviceAddress); env->ReleaseStringUTFChars(jDeviceAddress, nDeviceAddress); @@ -2227,8 +2115,8 @@ static jint convertAudioMixToNative(JNIEnv *env, nAudioMix->mVoiceCommunicationCaptureAllowed = env->GetBooleanField(jRule, gAudioMixingRuleFields.mVoiceCommunicationCaptureAllowed); env->DeleteLocalRef(jRule); - jobjectArray jCriteria = (jobjectArray)env->CallObjectMethod(jRuleCriteria, - gArrayListMethods.toArray); + jobjectArray jCriteria = static_cast<jobjectArray>( + env->CallObjectMethod(jRuleCriteria, gArrayListMethods.toArray)); env->DeleteLocalRef(jRuleCriteria); jint numCriteria = env->GetArrayLength(jCriteria); @@ -2264,8 +2152,8 @@ static jint convertAudioMixToNative(JNIEnv *env, auto paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jAttributes, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { - return jStatus; + if (jStatus != AUDIO_JAVA_SUCCESS) { + return jStatus; } if (match_rule == RULE_MATCH_ATTRIBUTE_USAGE) { nCriterion.mValue.mUsage = paa->usage; @@ -2283,7 +2171,7 @@ static jint convertAudioMixToNative(JNIEnv *env, env->DeleteLocalRef(jCriteria); - return (jint)AUDIO_JAVA_SUCCESS; + return AUDIO_JAVA_SUCCESS; } static jint @@ -2293,34 +2181,29 @@ android_media_AudioSystem_registerPolicyMixes(JNIEnv *env, jobject clazz, ALOGV("registerPolicyMixes"); if (jMixesList == NULL) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (!env->IsInstanceOf(jMixesList, gArrayListClass)) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } - jobjectArray jMixes = (jobjectArray)env->CallObjectMethod(jMixesList, - gArrayListMethods.toArray); + jobjectArray jMixes = + static_cast<jobjectArray>(env->CallObjectMethod(jMixesList, gArrayListMethods.toArray)); jint numMixes = env->GetArrayLength(jMixes); if (numMixes > MAX_MIXES_PER_POLICY) { numMixes = MAX_MIXES_PER_POLICY; } status_t status; - jint jStatus; - jobject jAudioMix = NULL; Vector <AudioMix> mixes; for (jint i = 0; i < numMixes; i++) { - jAudioMix = env->GetObjectArrayElement(jMixes, i); - if (!env->IsInstanceOf(jAudioMix, gAudioMixClass)) { - jStatus = (jint)AUDIO_JAVA_BAD_VALUE; - goto exit; + ScopedLocalRef<jobject> jAudioMix(env, env->GetObjectArrayElement(jMixes, i)); + if (!env->IsInstanceOf(jAudioMix.get(), gAudioMixClass)) { + return AUDIO_JAVA_BAD_VALUE; } AudioMix mix; - jStatus = convertAudioMixToNative(env, &mix, jAudioMix); - env->DeleteLocalRef(jAudioMix); - jAudioMix = NULL; - if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + if (jint jStatus = convertAudioMixToNative(env, &mix, jAudioMix.get()); + jStatus != AUDIO_JAVA_SUCCESS) { + return jStatus; } mixes.add(mix); } @@ -2329,16 +2212,7 @@ android_media_AudioSystem_registerPolicyMixes(JNIEnv *env, jobject clazz, status = AudioSystem::registerPolicyMixes(mixes, registration); ALOGV("AudioSystem::registerPolicyMixes() returned %d", status); - jStatus = nativeToJavaStatus(status); - if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; - } - -exit: - if (jAudioMix != NULL) { - env->DeleteLocalRef(jAudioMix); - } - return jStatus; + return nativeToJavaStatus(status); } static jint android_media_AudioSystem_setUidDeviceAffinities(JNIEnv *env, jobject clazz, @@ -2348,14 +2222,14 @@ static jint android_media_AudioSystem_setUidDeviceAffinities(JNIEnv *env, jobjec if (results != NO_ERROR) { return results; } - status_t status = AudioSystem::setUidDeviceAffinities((uid_t) uid, deviceVector); - return (jint) nativeToJavaStatus(status); + status_t status = AudioSystem::setUidDeviceAffinities(uid, deviceVector); + return nativeToJavaStatus(status); } static jint android_media_AudioSystem_removeUidDeviceAffinities(JNIEnv *env, jobject clazz, jint uid) { - status_t status = AudioSystem::removeUidDeviceAffinities((uid_t) uid); - return (jint) nativeToJavaStatus(status); + status_t status = AudioSystem::removeUidDeviceAffinities(static_cast<uid_t>(uid)); + return nativeToJavaStatus(status); } static jint android_media_AudioSystem_setUserIdDeviceAffinities(JNIEnv *env, jobject clazz, @@ -2366,14 +2240,14 @@ static jint android_media_AudioSystem_setUserIdDeviceAffinities(JNIEnv *env, job if (results != NO_ERROR) { return results; } - status_t status = AudioSystem::setUserIdDeviceAffinities((int)userId, deviceVector); - return (jint)nativeToJavaStatus(status); + status_t status = AudioSystem::setUserIdDeviceAffinities(userId, deviceVector); + return nativeToJavaStatus(status); } static jint android_media_AudioSystem_removeUserIdDeviceAffinities(JNIEnv *env, jobject clazz, jint userId) { - status_t status = AudioSystem::removeUserIdDeviceAffinities((int)userId); - return (jint)nativeToJavaStatus(status); + status_t status = AudioSystem::removeUserIdDeviceAffinities(userId); + return nativeToJavaStatus(status); } static jint @@ -2386,19 +2260,18 @@ static jfloat android_media_AudioSystem_getStreamVolumeDB(JNIEnv *env, jobject thiz, jint stream, jint index, jint device) { - return (jfloat)AudioSystem::getStreamVolumeDB((audio_stream_type_t)stream, - (int)index, - (audio_devices_t)device); + return AudioSystem::getStreamVolumeDB(static_cast<audio_stream_type_t>(stream), index, + static_cast<audio_devices_t>(device)); } static jint android_media_AudioSystem_getOffloadSupport(JNIEnv *env, jobject thiz, jint encoding, jint sampleRate, jint channelMask, jint channelIndexMask, jint streamType) { audio_offload_info_t format = AUDIO_INFO_INITIALIZER; - format.format = (audio_format_t) audioFormatToNative(encoding); - format.sample_rate = (uint32_t) sampleRate; + format.format = static_cast<audio_format_t>(audioFormatToNative(encoding)); + format.sample_rate = sampleRate; format.channel_mask = nativeChannelMaskFromJavaChannelMasks(channelMask, channelIndexMask); - format.stream_type = (audio_stream_type_t) streamType; + format.stream_type = static_cast<audio_stream_type_t>(streamType); format.has_video = false; format.is_streaming = false; // offload duration unknown at this point: @@ -2415,11 +2288,11 @@ android_media_AudioSystem_getMicrophones(JNIEnv *env, jobject thiz, jobject jMic if (jMicrophonesInfo == NULL) { ALOGE("jMicrophonesInfo NULL MicrophoneInfo ArrayList"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (!env->IsInstanceOf(jMicrophonesInfo, gArrayListClass)) { ALOGE("getMicrophones not an arraylist"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } jint jStatus; @@ -2431,7 +2304,7 @@ android_media_AudioSystem_getMicrophones(JNIEnv *env, jobject thiz, jobject jMic return jStatus; } if (microphones.size() == 0) { - jStatus = (jint)AUDIO_JAVA_SUCCESS; + jStatus = AUDIO_JAVA_SUCCESS; return jStatus; } for (size_t i = 0; i < microphones.size(); i++) { @@ -2453,7 +2326,7 @@ static jint android_media_AudioSystem_getHwOffloadFormatsSupportedForBluetoothMe jint jStatus = AUDIO_JAVA_SUCCESS; if (!env->IsInstanceOf(jEncodingFormatList, gArrayListClass)) { ALOGE("%s: jEncodingFormatList not an ArrayList", __FUNCTION__); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } std::vector<audio_format_t> encodingFormats; status_t status = @@ -2572,12 +2445,12 @@ static jint android_media_AudioSystem_setSurroundFormatEnabled(JNIEnv *env, jobject thiz, jint audioFormat, jboolean enabled) { - status_t status = AudioSystem::setSurroundFormatEnabled(audioFormatToNative(audioFormat), - (bool)enabled); + status_t status = + AudioSystem::setSurroundFormatEnabled(audioFormatToNative(audioFormat), enabled); if (status != NO_ERROR) { ALOGE_IF(status != NO_ERROR, "AudioSystem::setSurroundFormatEnabled error %d", status); } - return (jint)nativeToJavaStatus(status); + return nativeToJavaStatus(status); } static jint android_media_AudioSystem_getMaxChannelCount(JNIEnv *env, jobject thiz) { @@ -2618,7 +2491,7 @@ static jint android_media_AudioSystem_setAssistantServicesUids(JNIEnv *env, jobj status_t status = AudioSystem::setAssistantServicesUids(nativeUidsVector); - return (jint)nativeToJavaStatus(status); + return nativeToJavaStatus(status); } static jint android_media_AudioSystem_setActiveAssistantServicesUids(JNIEnv *env, jobject thiz, @@ -2627,7 +2500,7 @@ static jint android_media_AudioSystem_setActiveAssistantServicesUids(JNIEnv *env status_t status = AudioSystem::setActiveAssistantServicesUids(nativeActiveUidsVector); - return (jint)nativeToJavaStatus(status); + return nativeToJavaStatus(status); } static jint @@ -2635,12 +2508,12 @@ android_media_AudioSystem_setA11yServicesUids(JNIEnv *env, jobject thiz, jintArr std::vector<uid_t> nativeUidsVector = convertJIntArrayToUidVector(env, uids); status_t status = AudioSystem::setA11yServicesUids(nativeUidsVector); - return (jint)nativeToJavaStatus(status); + return nativeToJavaStatus(status); } static jint android_media_AudioSystem_setCurrentImeUid(JNIEnv *env, jobject thiz, jint uid) { status_t status = AudioSystem::setCurrentImeUid(uid); - return (jint)nativeToJavaStatus(status); + return nativeToJavaStatus(status); } static jboolean @@ -2658,7 +2531,7 @@ static jint android_media_AudioSystem_setSupportedSystemUsages(JNIEnv *env, jobj std::vector<audio_usage_t> nativeSystemUsagesVector; if (systemUsages == nullptr) { - return (jint) AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } int *nativeSystemUsages = nullptr; @@ -2675,7 +2548,7 @@ static jint android_media_AudioSystem_setSupportedSystemUsages(JNIEnv *env, jobj } status_t status = AudioSystem::setSupportedSystemUsages(nativeSystemUsagesVector); - return (jint)nativeToJavaStatus(status); + return nativeToJavaStatus(status); } static jint @@ -2686,16 +2559,16 @@ android_media_AudioSystem_setAllowedCapturePolicy(JNIEnv *env, jobject thiz, jin static jint android_media_AudioSystem_setRttEnabled(JNIEnv *env, jobject thiz, jboolean enabled) { - return (jint) check_AudioSystem_Command(AudioSystem::setRttEnabled(enabled)); + return check_AudioSystem_Command(AudioSystem::setRttEnabled(enabled)); } static jint android_media_AudioSystem_setAudioHalPids(JNIEnv *env, jobject clazz, jintArray jPids) { if (jPids == NULL) { - return (jint) AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } - pid_t *nPidsArray = (pid_t *) env->GetIntArrayElements(jPids, NULL); + pid_t *nPidsArray = reinterpret_cast<pid_t *>(env->GetIntArrayElements(jPids, NULL)); std::vector<pid_t> nPids(nPidsArray, nPidsArray + env->GetArrayLength(jPids)); status_t status = AudioSystem::setAudioHalPids(nPids); env->ReleaseIntArrayElements(jPids, nPidsArray, 0); @@ -2719,9 +2592,9 @@ static jint android_media_AudioSystem_setDevicesRoleForStrategy(JNIEnv *env, job return results; } int status = check_AudioSystem_Command( - AudioSystem::setDevicesRoleForStrategy((product_strategy_t)strategy, - (device_role_t)role, nDevices)); - return (jint) status; + AudioSystem::setDevicesRoleForStrategy(static_cast<product_strategy_t>(strategy), + static_cast<device_role_t>(role), nDevices)); + return status; } static jint android_media_AudioSystem_removeDevicesRoleForStrategy(JNIEnv *env, jobject thiz, @@ -2734,8 +2607,8 @@ static jint android_media_AudioSystem_removeDevicesRoleForStrategy(JNIEnv *env, return results; } int status = check_AudioSystem_Command( - AudioSystem::removeDevicesRoleForStrategy((product_strategy_t)strategy, - (device_role_t)role, nDevices)); + AudioSystem::removeDevicesRoleForStrategy(static_cast<product_strategy_t>(strategy), + static_cast<device_role_t>(role), nDevices)); return (jint)status; } @@ -2753,10 +2626,10 @@ static jint android_media_AudioSystem_getDevicesForRoleAndStrategy(JNIEnv *env, jobject jDevices) { AudioDeviceTypeAddrVector nDevices; status_t status = check_AudioSystem_Command( - AudioSystem::getDevicesForRoleAndStrategy((product_strategy_t)strategy, - (device_role_t)role, nDevices)); + AudioSystem::getDevicesForRoleAndStrategy(static_cast<product_strategy_t>(strategy), + static_cast<device_role_t>(role), nDevices)); if (status != NO_ERROR) { - return (jint) status; + return status; } for (const auto &device : nDevices) { jobject jAudioDeviceAttributes = NULL; @@ -2779,9 +2652,10 @@ static jint android_media_AudioSystem_setDevicesRoleForCapturePreset( return results; } int status = check_AudioSystem_Command( - AudioSystem::setDevicesRoleForCapturePreset((audio_source_t)capturePreset, - (device_role_t)role, nDevices)); - return (jint)status; + AudioSystem::setDevicesRoleForCapturePreset(static_cast<audio_source_t>(capturePreset), + static_cast<device_role_t>(role), + nDevices)); + return status; } static jint android_media_AudioSystem_addDevicesRoleForCapturePreset( @@ -2793,9 +2667,10 @@ static jint android_media_AudioSystem_addDevicesRoleForCapturePreset( return results; } int status = check_AudioSystem_Command( - AudioSystem::addDevicesRoleForCapturePreset((audio_source_t)capturePreset, - (device_role_t)role, nDevices)); - return (jint)status; + AudioSystem::addDevicesRoleForCapturePreset(static_cast<audio_source_t>(capturePreset), + static_cast<device_role_t>(role), + nDevices)); + return status; } static jint android_media_AudioSystem_removeDevicesRoleForCapturePreset( @@ -2807,17 +2682,20 @@ static jint android_media_AudioSystem_removeDevicesRoleForCapturePreset( return results; } int status = check_AudioSystem_Command( - AudioSystem::removeDevicesRoleForCapturePreset((audio_source_t)capturePreset, - (device_role_t)role, nDevices)); - return (jint)status; + AudioSystem::removeDevicesRoleForCapturePreset(static_cast<audio_source_t>( + capturePreset), + static_cast<device_role_t>(role), + nDevices)); + return status; } static jint android_media_AudioSystem_clearDevicesRoleForCapturePreset(JNIEnv *env, jobject thiz, jint capturePreset, jint role) { - return (jint)check_AudioSystem_Command( - AudioSystem::clearDevicesRoleForCapturePreset((audio_source_t)capturePreset, - (device_role_t)role)); + return static_cast<jint>(check_AudioSystem_Command( + AudioSystem::clearDevicesRoleForCapturePreset(static_cast<audio_source_t>( + capturePreset), + static_cast<device_role_t>(role)))); } static jint android_media_AudioSystem_getDevicesForRoleAndCapturePreset(JNIEnv *env, jobject thiz, @@ -2826,10 +2704,12 @@ static jint android_media_AudioSystem_getDevicesForRoleAndCapturePreset(JNIEnv * jobject jDevices) { AudioDeviceTypeAddrVector nDevices; status_t status = check_AudioSystem_Command( - AudioSystem::getDevicesForRoleAndCapturePreset((audio_source_t)capturePreset, - (device_role_t)role, nDevices)); + AudioSystem::getDevicesForRoleAndCapturePreset(static_cast<audio_source_t>( + capturePreset), + static_cast<device_role_t>(role), + nDevices)); if (status != NO_ERROR) { - return (jint)status; + return status; } for (const auto &device : nDevices) { jobject jAudioDeviceAttributes = NULL; @@ -2854,12 +2734,12 @@ static jint android_media_AudioSystem_getDevicesForAttributes(JNIEnv *env, jobje // components call this method often if (jDeviceArray == nullptr || maxResultSize == 0) { ALOGE("%s invalid array to store AudioDeviceAttributes", __FUNCTION__); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); - if (jStatus != (jint) AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return jStatus; } @@ -2888,7 +2768,7 @@ static jint android_media_AudioSystem_getDevicesForAttributes(JNIEnv *env, jobje static jint android_media_AudioSystem_setVibratorInfos(JNIEnv *env, jobject thiz, jobject jVibrators) { if (!env->IsInstanceOf(jVibrators, gListClass)) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } const jint size = env->CallIntMethod(jVibrators, gListMethods.size); std::vector<media::AudioVibratorInfo> vibratorInfos; @@ -2896,7 +2776,7 @@ static jint android_media_AudioSystem_setVibratorInfos(JNIEnv *env, jobject thiz ScopedLocalRef<jobject> jVibrator(env, env->CallObjectMethod(jVibrators, gListMethods.get, i)); if (!env->IsInstanceOf(jVibrator.get(), gVibratorClass)) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } media::AudioVibratorInfo vibratorInfo; vibratorInfo.id = env->CallIntMethod(jVibrator.get(), gVibratorMethods.getId); @@ -2907,7 +2787,7 @@ static jint android_media_AudioSystem_setVibratorInfos(JNIEnv *env, jobject thiz env->CallFloatMethod(jVibrator.get(), gVibratorMethods.getMaxAmplitude); vibratorInfos.push_back(vibratorInfo); } - return (jint)check_AudioSystem_Command(AudioSystem::setVibratorInfos(vibratorInfos)); + return check_AudioSystem_Command(AudioSystem::setVibratorInfos(vibratorInfos)); } static jobject android_media_AudioSystem_getSpatializer(JNIEnv *env, jobject thiz, @@ -2929,8 +2809,8 @@ static jboolean android_media_AudioSystem_canBeSpatialized(JNIEnv *env, jobject jobjectArray jDeviceArray) { JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { - return false; + if (jStatus != AUDIO_JAVA_SUCCESS) { + return false; } AudioDeviceTypeAddrVector nDevices; @@ -2943,7 +2823,7 @@ static jboolean android_media_AudioSystem_canBeSpatialized(JNIEnv *env, jobject return false; } jStatus = createAudioDeviceTypeAddrFromJava(env, &device, jDevice); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return false; } nDevices.push_back(device); @@ -3000,7 +2880,7 @@ static jint android_media_AudioSystem_getDirectPlaybackSupport(JNIEnv *env, jobj jobject jFormat, jobject jaa) { JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return DIRECT_NOT_SUPPORTED; } @@ -3023,20 +2903,20 @@ static jint android_media_AudioSystem_getDirectProfilesForAttributes(JNIEnv *env if (jAudioAttributes == nullptr) { ALOGE("jAudioAttributes is NULL"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (jAudioProfilesList == nullptr) { ALOGE("jAudioProfilesList is NULL"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (!env->IsInstanceOf(jAudioProfilesList, gArrayListClass)) { ALOGE("jAudioProfilesList not an ArrayList"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jAudioAttributes, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return jStatus; } @@ -3212,8 +3092,7 @@ static jboolean android_media_AudioSystem_supportsBluetoothVariableLatency(JNIEn static int android_media_AudioSystem_setBluetoothVariableLatencyEnabled(JNIEnv *env, jobject thiz, jboolean enabled) { - return (jint)check_AudioSystem_Command( - AudioSystem::setBluetoothVariableLatencyEnabled(enabled)); + return check_AudioSystem_Command(AudioSystem::setBluetoothVariableLatencyEnabled(enabled)); } static jboolean android_media_AudioSystem_isBluetoothVariableLatencyEnabled(JNIEnv *env, @@ -3227,191 +3106,182 @@ static jboolean android_media_AudioSystem_isBluetoothVariableLatencyEnabled(JNIE // ---------------------------------------------------------------------------- -static const JNINativeMethod gMethods[] = - {{"setParameters", "(Ljava/lang/String;)I", - (void *)android_media_AudioSystem_setParameters}, - {"getParameters", "(Ljava/lang/String;)Ljava/lang/String;", - (void *)android_media_AudioSystem_getParameters}, - {"muteMicrophone", "(Z)I", (void *)android_media_AudioSystem_muteMicrophone}, - {"isMicrophoneMuted", "()Z", (void *)android_media_AudioSystem_isMicrophoneMuted}, - {"isStreamActive", "(II)Z", (void *)android_media_AudioSystem_isStreamActive}, - {"isStreamActiveRemotely", "(II)Z", - (void *)android_media_AudioSystem_isStreamActiveRemotely}, - {"isSourceActive", "(I)Z", (void *)android_media_AudioSystem_isSourceActive}, - {"newAudioSessionId", "()I", (void *)android_media_AudioSystem_newAudioSessionId}, - {"newAudioPlayerId", "()I", (void *)android_media_AudioSystem_newAudioPlayerId}, - {"newAudioRecorderId", "()I", (void *)android_media_AudioSystem_newAudioRecorderId}, - {"setDeviceConnectionState", "(ILandroid/os/Parcel;I)I", - (void *)android_media_AudioSystem_setDeviceConnectionState}, - {"getDeviceConnectionState", "(ILjava/lang/String;)I", - (void *)android_media_AudioSystem_getDeviceConnectionState}, - {"handleDeviceConfigChange", "(ILjava/lang/String;Ljava/lang/String;I)I", - (void *)android_media_AudioSystem_handleDeviceConfigChange}, - {"setPhoneState", "(II)I", (void *)android_media_AudioSystem_setPhoneState}, - {"setForceUse", "(II)I", (void *)android_media_AudioSystem_setForceUse}, - {"getForceUse", "(I)I", (void *)android_media_AudioSystem_getForceUse}, - {"initStreamVolume", "(III)I", (void *)android_media_AudioSystem_initStreamVolume}, - {"setStreamVolumeIndex", "(III)I", (void *)android_media_AudioSystem_setStreamVolumeIndex}, - {"getStreamVolumeIndex", "(II)I", (void *)android_media_AudioSystem_getStreamVolumeIndex}, - {"setVolumeIndexForAttributes", "(Landroid/media/AudioAttributes;II)I", - (void *)android_media_AudioSystem_setVolumeIndexForAttributes}, - {"getVolumeIndexForAttributes", "(Landroid/media/AudioAttributes;I)I", - (void *)android_media_AudioSystem_getVolumeIndexForAttributes}, - {"getMinVolumeIndexForAttributes", "(Landroid/media/AudioAttributes;)I", - (void *)android_media_AudioSystem_getMinVolumeIndexForAttributes}, - {"getMaxVolumeIndexForAttributes", "(Landroid/media/AudioAttributes;)I", - (void *)android_media_AudioSystem_getMaxVolumeIndexForAttributes}, - {"setMasterVolume", "(F)I", (void *)android_media_AudioSystem_setMasterVolume}, - {"getMasterVolume", "()F", (void *)android_media_AudioSystem_getMasterVolume}, - {"setMasterMute", "(Z)I", (void *)android_media_AudioSystem_setMasterMute}, - {"getMasterMute", "()Z", (void *)android_media_AudioSystem_getMasterMute}, - {"setMasterMono", "(Z)I", (void *)android_media_AudioSystem_setMasterMono}, - {"getMasterMono", "()Z", (void *)android_media_AudioSystem_getMasterMono}, - {"setMasterBalance", "(F)I", (void *)android_media_AudioSystem_setMasterBalance}, - {"getMasterBalance", "()F", (void *)android_media_AudioSystem_getMasterBalance}, - {"getPrimaryOutputSamplingRate", "()I", - (void *)android_media_AudioSystem_getPrimaryOutputSamplingRate}, - {"getPrimaryOutputFrameCount", "()I", - (void *)android_media_AudioSystem_getPrimaryOutputFrameCount}, - {"getOutputLatency", "(I)I", (void *)android_media_AudioSystem_getOutputLatency}, - {"setLowRamDevice", "(ZJ)I", (void *)android_media_AudioSystem_setLowRamDevice}, - {"checkAudioFlinger", "()I", (void *)android_media_AudioSystem_checkAudioFlinger}, - {"setAudioFlingerBinder", "(Landroid/os/IBinder;)V", - (void *)android_media_AudioSystem_setAudioFlingerBinder}, - {"listAudioPorts", "(Ljava/util/ArrayList;[I)I", - (void *)android_media_AudioSystem_listAudioPorts}, - {"createAudioPatch", - "([Landroid/media/AudioPatch;[Landroid/media/AudioPortConfig;[Landroid/media/" - "AudioPortConfig;)I", - (void *)android_media_AudioSystem_createAudioPatch}, - {"releaseAudioPatch", "(Landroid/media/AudioPatch;)I", - (void *)android_media_AudioSystem_releaseAudioPatch}, - {"listAudioPatches", "(Ljava/util/ArrayList;[I)I", - (void *)android_media_AudioSystem_listAudioPatches}, - {"setAudioPortConfig", "(Landroid/media/AudioPortConfig;)I", - (void *)android_media_AudioSystem_setAudioPortConfig}, - {"startAudioSource", "(Landroid/media/AudioPortConfig;Landroid/media/AudioAttributes;)I", - (void *)android_media_AudioSystem_startAudioSource}, - {"stopAudioSource", "(I)I", (void *)android_media_AudioSystem_stopAudioSource}, - {"getAudioHwSyncForSession", "(I)I", - (void *)android_media_AudioSystem_getAudioHwSyncForSession}, - {"registerPolicyMixes", "(Ljava/util/ArrayList;Z)I", - (void *)android_media_AudioSystem_registerPolicyMixes}, - {"setUidDeviceAffinities", "(I[I[Ljava/lang/String;)I", - (void *)android_media_AudioSystem_setUidDeviceAffinities}, - {"removeUidDeviceAffinities", "(I)I", - (void *)android_media_AudioSystem_removeUidDeviceAffinities}, - {"native_register_dynamic_policy_callback", "()V", - (void *)android_media_AudioSystem_registerDynPolicyCallback}, - {"native_register_recording_callback", "()V", - (void *)android_media_AudioSystem_registerRecordingCallback}, - {"native_register_routing_callback", "()V", - (void *)android_media_AudioSystem_registerRoutingCallback}, - {"native_register_vol_range_init_req_callback", "()V", - (void *)android_media_AudioSystem_registerVolRangeInitReqCallback}, - {"systemReady", "()I", (void *)android_media_AudioSystem_systemReady}, - {"getStreamVolumeDB", "(III)F", (void *)android_media_AudioSystem_getStreamVolumeDB}, - {"native_get_offload_support", "(IIIII)I", - (void *)android_media_AudioSystem_getOffloadSupport}, - {"getMicrophones", "(Ljava/util/ArrayList;)I", - (void *)android_media_AudioSystem_getMicrophones}, - {"getSurroundFormats", "(Ljava/util/Map;)I", - (void *)android_media_AudioSystem_getSurroundFormats}, - {"getReportedSurroundFormats", "(Ljava/util/ArrayList;)I", - (void *)android_media_AudioSystem_getReportedSurroundFormats}, - {"setSurroundFormatEnabled", "(IZ)I", - (void *)android_media_AudioSystem_setSurroundFormatEnabled}, - {"setAssistantServicesUids", "([I)I", - (void *)android_media_AudioSystem_setAssistantServicesUids}, - {"setActiveAssistantServicesUids", "([I)I", - (void *)android_media_AudioSystem_setActiveAssistantServicesUids}, - {"setA11yServicesUids", "([I)I", (void *)android_media_AudioSystem_setA11yServicesUids}, - {"isHapticPlaybackSupported", "()Z", - (void *)android_media_AudioSystem_isHapticPlaybackSupported}, - {"isUltrasoundSupported", "()Z", (void *)android_media_AudioSystem_isUltrasoundSupported}, - {"getHwOffloadFormatsSupportedForBluetoothMedia", "(ILjava/util/ArrayList;)I", - (void *)android_media_AudioSystem_getHwOffloadFormatsSupportedForBluetoothMedia}, - {"setSupportedSystemUsages", "([I)I", - (void *)android_media_AudioSystem_setSupportedSystemUsages}, - {"setAllowedCapturePolicy", "(II)I", - (void *)android_media_AudioSystem_setAllowedCapturePolicy}, - {"setRttEnabled", "(Z)I", (void *)android_media_AudioSystem_setRttEnabled}, - {"setAudioHalPids", "([I)I", (void *)android_media_AudioSystem_setAudioHalPids}, - {"isCallScreeningModeSupported", "()Z", - (void *)android_media_AudioSystem_isCallScreeningModeSupported}, - {"setDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I", - (void *)android_media_AudioSystem_setDevicesRoleForStrategy}, - {"removeDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I", - (void *)android_media_AudioSystem_removeDevicesRoleForStrategy}, - {"clearDevicesRoleForStrategy", "(II)I", - (void *)android_media_AudioSystem_clearDevicesRoleForStrategy}, - {"getDevicesForRoleAndStrategy", "(IILjava/util/List;)I", - (void *)android_media_AudioSystem_getDevicesForRoleAndStrategy}, - {"setDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", - (void *)android_media_AudioSystem_setDevicesRoleForCapturePreset}, - {"addDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", - (void *)android_media_AudioSystem_addDevicesRoleForCapturePreset}, - {"removeDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", - (void *)android_media_AudioSystem_removeDevicesRoleForCapturePreset}, - {"clearDevicesRoleForCapturePreset", "(II)I", - (void *)android_media_AudioSystem_clearDevicesRoleForCapturePreset}, - {"getDevicesForRoleAndCapturePreset", "(IILjava/util/List;)I", - (void *)android_media_AudioSystem_getDevicesForRoleAndCapturePreset}, - {"getDevicesForAttributes", - "(Landroid/media/AudioAttributes;[Landroid/media/AudioDeviceAttributes;Z)I", - (void *)android_media_AudioSystem_getDevicesForAttributes}, - {"setUserIdDeviceAffinities", "(I[I[Ljava/lang/String;)I", - (void *)android_media_AudioSystem_setUserIdDeviceAffinities}, - {"removeUserIdDeviceAffinities", "(I)I", - (void *)android_media_AudioSystem_removeUserIdDeviceAffinities}, - {"setCurrentImeUid", "(I)I", (void *)android_media_AudioSystem_setCurrentImeUid}, - {"setVibratorInfos", "(Ljava/util/List;)I", - (void *)android_media_AudioSystem_setVibratorInfos}, - {"nativeGetSpatializer", - "(Landroid/media/INativeSpatializerCallback;)Landroid/os/IBinder;", - (void *)android_media_AudioSystem_getSpatializer}, - {"canBeSpatialized", - "(Landroid/media/AudioAttributes;Landroid/media/AudioFormat;" - "[Landroid/media/AudioDeviceAttributes;)Z", - (void *)android_media_AudioSystem_canBeSpatialized}, - {"nativeGetSoundDose", "(Landroid/media/ISoundDoseCallback;)Landroid/os/IBinder;", - (void *)android_media_AudioSystem_nativeGetSoundDose}, - {"getDirectPlaybackSupport", - "(Landroid/media/AudioFormat;Landroid/media/AudioAttributes;)I", - (void *)android_media_AudioSystem_getDirectPlaybackSupport}, - {"getDirectProfilesForAttributes", - "(Landroid/media/AudioAttributes;Ljava/util/ArrayList;)I", - (void *)android_media_AudioSystem_getDirectProfilesForAttributes}, - {"getSupportedMixerAttributes", "(ILjava/util/List;)I", - (void *)android_media_AudioSystem_getSupportedMixerAttributes}, - {"setPreferredMixerAttributes", - "(Landroid/media/AudioAttributes;IILandroid/media/AudioMixerAttributes;)I", - (void *)android_media_AudioSystem_setPreferredMixerAttributes}, - {"getPreferredMixerAttributes", "(Landroid/media/AudioAttributes;ILjava/util/List;)I", - (void *)android_media_AudioSystem_getPreferredMixerAttributes}, - {"clearPreferredMixerAttributes", "(Landroid/media/AudioAttributes;II)I", - (void *)android_media_AudioSystem_clearPreferredMixerAttributes}, - {"supportsBluetoothVariableLatency", "()Z", - (void *)android_media_AudioSystem_supportsBluetoothVariableLatency}, - {"setBluetoothVariableLatencyEnabled", "(Z)I", - (void *)android_media_AudioSystem_setBluetoothVariableLatencyEnabled}, - {"isBluetoothVariableLatencyEnabled", "()Z", - (void *)android_media_AudioSystem_isBluetoothVariableLatencyEnabled}}; - -static const JNINativeMethod gEventHandlerMethods[] = { - {"native_setup", - "(Ljava/lang/Object;)V", - (void *)android_media_AudioSystem_eventHandlerSetup}, - {"native_finalize", - "()V", - (void *)android_media_AudioSystem_eventHandlerFinalize}, -}; +#define MAKE_AUDIO_SYSTEM_METHOD(x) \ + MAKE_JNI_NATIVE_METHOD_AUTOSIG(#x, android_media_AudioSystem_##x) -static const JNINativeMethod gFrameworkCapabilities[] = { - {"native_getMaxChannelCount", "()I", (void *)android_media_AudioSystem_getMaxChannelCount}, - {"native_getMaxSampleRate", "()I", (void *)android_media_AudioSystem_getMaxSampleRate}, - {"native_getMinSampleRate", "()I", (void *)android_media_AudioSystem_getMinSampleRate}, -}; +static const JNINativeMethod gMethods[] = + {MAKE_AUDIO_SYSTEM_METHOD(setParameters), + MAKE_AUDIO_SYSTEM_METHOD(getParameters), + MAKE_AUDIO_SYSTEM_METHOD(muteMicrophone), + MAKE_AUDIO_SYSTEM_METHOD(isMicrophoneMuted), + MAKE_AUDIO_SYSTEM_METHOD(isStreamActive), + MAKE_AUDIO_SYSTEM_METHOD(isStreamActiveRemotely), + MAKE_AUDIO_SYSTEM_METHOD(isSourceActive), + MAKE_AUDIO_SYSTEM_METHOD(newAudioSessionId), + MAKE_AUDIO_SYSTEM_METHOD(newAudioPlayerId), + MAKE_AUDIO_SYSTEM_METHOD(newAudioRecorderId), + MAKE_JNI_NATIVE_METHOD("setDeviceConnectionState", "(ILandroid/os/Parcel;I)I", + android_media_AudioSystem_setDeviceConnectionState), + MAKE_AUDIO_SYSTEM_METHOD(getDeviceConnectionState), + MAKE_AUDIO_SYSTEM_METHOD(handleDeviceConfigChange), + MAKE_AUDIO_SYSTEM_METHOD(setPhoneState), + MAKE_AUDIO_SYSTEM_METHOD(setForceUse), + MAKE_AUDIO_SYSTEM_METHOD(getForceUse), + MAKE_AUDIO_SYSTEM_METHOD(initStreamVolume), + MAKE_AUDIO_SYSTEM_METHOD(setStreamVolumeIndex), + MAKE_AUDIO_SYSTEM_METHOD(getStreamVolumeIndex), + MAKE_JNI_NATIVE_METHOD("setVolumeIndexForAttributes", + "(Landroid/media/AudioAttributes;II)I", + android_media_AudioSystem_setVolumeIndexForAttributes), + MAKE_JNI_NATIVE_METHOD("getVolumeIndexForAttributes", + "(Landroid/media/AudioAttributes;I)I", + android_media_AudioSystem_getVolumeIndexForAttributes), + MAKE_JNI_NATIVE_METHOD("getMinVolumeIndexForAttributes", + "(Landroid/media/AudioAttributes;)I", + android_media_AudioSystem_getMinVolumeIndexForAttributes), + MAKE_JNI_NATIVE_METHOD("getMaxVolumeIndexForAttributes", + "(Landroid/media/AudioAttributes;)I", + android_media_AudioSystem_getMaxVolumeIndexForAttributes), + MAKE_AUDIO_SYSTEM_METHOD(setMasterVolume), + MAKE_AUDIO_SYSTEM_METHOD(getMasterVolume), + MAKE_AUDIO_SYSTEM_METHOD(setMasterMute), + MAKE_AUDIO_SYSTEM_METHOD(getMasterMute), + MAKE_AUDIO_SYSTEM_METHOD(setMasterMono), + MAKE_AUDIO_SYSTEM_METHOD(getMasterMono), + MAKE_AUDIO_SYSTEM_METHOD(setMasterBalance), + MAKE_AUDIO_SYSTEM_METHOD(getMasterBalance), + MAKE_AUDIO_SYSTEM_METHOD(getPrimaryOutputSamplingRate), + MAKE_AUDIO_SYSTEM_METHOD(getPrimaryOutputFrameCount), + MAKE_AUDIO_SYSTEM_METHOD(getOutputLatency), + MAKE_AUDIO_SYSTEM_METHOD(setLowRamDevice), + MAKE_AUDIO_SYSTEM_METHOD(checkAudioFlinger), + MAKE_JNI_NATIVE_METHOD("setAudioFlingerBinder", "(Landroid/os/IBinder;)V", + android_media_AudioSystem_setAudioFlingerBinder), + MAKE_JNI_NATIVE_METHOD("listAudioPorts", "(Ljava/util/ArrayList;[I)I", + android_media_AudioSystem_listAudioPorts), + MAKE_JNI_NATIVE_METHOD("createAudioPatch", + "([Landroid/media/AudioPatch;[Landroid/media/" + "AudioPortConfig;[Landroid/media/AudioPortConfig;)I", + android_media_AudioSystem_createAudioPatch), + MAKE_JNI_NATIVE_METHOD("releaseAudioPatch", "(Landroid/media/AudioPatch;)I", + android_media_AudioSystem_releaseAudioPatch), + MAKE_JNI_NATIVE_METHOD("listAudioPatches", "(Ljava/util/ArrayList;[I)I", + android_media_AudioSystem_listAudioPatches), + MAKE_JNI_NATIVE_METHOD("setAudioPortConfig", "(Landroid/media/AudioPortConfig;)I", + android_media_AudioSystem_setAudioPortConfig), + MAKE_JNI_NATIVE_METHOD("startAudioSource", + "(Landroid/media/AudioPortConfig;Landroid/media/AudioAttributes;)I", + android_media_AudioSystem_startAudioSource), + MAKE_AUDIO_SYSTEM_METHOD(stopAudioSource), + MAKE_AUDIO_SYSTEM_METHOD(getAudioHwSyncForSession), + MAKE_JNI_NATIVE_METHOD("registerPolicyMixes", "(Ljava/util/ArrayList;Z)I", + android_media_AudioSystem_registerPolicyMixes), + MAKE_JNI_NATIVE_METHOD("setUidDeviceAffinities", "(I[I[Ljava/lang/String;)I", + android_media_AudioSystem_setUidDeviceAffinities), + MAKE_AUDIO_SYSTEM_METHOD(removeUidDeviceAffinities), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_dynamic_policy_callback", + android_media_AudioSystem_registerDynPolicyCallback), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_recording_callback", + android_media_AudioSystem_registerRecordingCallback), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_routing_callback", + android_media_AudioSystem_registerRoutingCallback), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_vol_range_init_req_callback", + android_media_AudioSystem_registerVolRangeInitReqCallback), + MAKE_AUDIO_SYSTEM_METHOD(systemReady), + MAKE_AUDIO_SYSTEM_METHOD(getStreamVolumeDB), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_get_offload_support", + android_media_AudioSystem_getOffloadSupport), + MAKE_JNI_NATIVE_METHOD("getMicrophones", "(Ljava/util/ArrayList;)I", + android_media_AudioSystem_getMicrophones), + MAKE_JNI_NATIVE_METHOD("getSurroundFormats", "(Ljava/util/Map;)I", + android_media_AudioSystem_getSurroundFormats), + MAKE_JNI_NATIVE_METHOD("getReportedSurroundFormats", "(Ljava/util/ArrayList;)I", + android_media_AudioSystem_getReportedSurroundFormats), + MAKE_AUDIO_SYSTEM_METHOD(setSurroundFormatEnabled), + MAKE_AUDIO_SYSTEM_METHOD(setAssistantServicesUids), + MAKE_AUDIO_SYSTEM_METHOD(setActiveAssistantServicesUids), + MAKE_AUDIO_SYSTEM_METHOD(setA11yServicesUids), + MAKE_AUDIO_SYSTEM_METHOD(isHapticPlaybackSupported), + MAKE_AUDIO_SYSTEM_METHOD(isUltrasoundSupported), + MAKE_JNI_NATIVE_METHOD( + "getHwOffloadFormatsSupportedForBluetoothMedia", "(ILjava/util/ArrayList;)I", + android_media_AudioSystem_getHwOffloadFormatsSupportedForBluetoothMedia), + MAKE_AUDIO_SYSTEM_METHOD(setSupportedSystemUsages), + MAKE_AUDIO_SYSTEM_METHOD(setAllowedCapturePolicy), + MAKE_AUDIO_SYSTEM_METHOD(setRttEnabled), + MAKE_AUDIO_SYSTEM_METHOD(setAudioHalPids), + MAKE_AUDIO_SYSTEM_METHOD(isCallScreeningModeSupported), + MAKE_JNI_NATIVE_METHOD("setDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I", + android_media_AudioSystem_setDevicesRoleForStrategy), + MAKE_JNI_NATIVE_METHOD("removeDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I", + android_media_AudioSystem_removeDevicesRoleForStrategy), + MAKE_AUDIO_SYSTEM_METHOD(clearDevicesRoleForStrategy), + MAKE_JNI_NATIVE_METHOD("getDevicesForRoleAndStrategy", "(IILjava/util/List;)I", + android_media_AudioSystem_getDevicesForRoleAndStrategy), + MAKE_JNI_NATIVE_METHOD("setDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", + android_media_AudioSystem_setDevicesRoleForCapturePreset), + MAKE_JNI_NATIVE_METHOD("addDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", + android_media_AudioSystem_addDevicesRoleForCapturePreset), + MAKE_JNI_NATIVE_METHOD("removeDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", + android_media_AudioSystem_removeDevicesRoleForCapturePreset), + MAKE_AUDIO_SYSTEM_METHOD(clearDevicesRoleForCapturePreset), + MAKE_JNI_NATIVE_METHOD("getDevicesForRoleAndCapturePreset", "(IILjava/util/List;)I", + android_media_AudioSystem_getDevicesForRoleAndCapturePreset), + MAKE_JNI_NATIVE_METHOD("getDevicesForAttributes", + "(Landroid/media/AudioAttributes;[Landroid/media/" + "AudioDeviceAttributes;Z)I", + android_media_AudioSystem_getDevicesForAttributes), + MAKE_JNI_NATIVE_METHOD("setUserIdDeviceAffinities", "(I[I[Ljava/lang/String;)I", + android_media_AudioSystem_setUserIdDeviceAffinities), + MAKE_AUDIO_SYSTEM_METHOD(removeUserIdDeviceAffinities), + MAKE_AUDIO_SYSTEM_METHOD(setCurrentImeUid), + MAKE_JNI_NATIVE_METHOD("setVibratorInfos", "(Ljava/util/List;)I", + android_media_AudioSystem_setVibratorInfos), + MAKE_JNI_NATIVE_METHOD("nativeGetSpatializer", + "(Landroid/media/INativeSpatializerCallback;)Landroid/os/IBinder;", + android_media_AudioSystem_getSpatializer), + MAKE_JNI_NATIVE_METHOD("canBeSpatialized", + "(Landroid/media/AudioAttributes;Landroid/media/AudioFormat;" + "[Landroid/media/AudioDeviceAttributes;)Z", + android_media_AudioSystem_canBeSpatialized), + MAKE_JNI_NATIVE_METHOD("nativeGetSoundDose", + "(Landroid/media/ISoundDoseCallback;)Landroid/os/IBinder;", + android_media_AudioSystem_nativeGetSoundDose), + MAKE_JNI_NATIVE_METHOD("getDirectPlaybackSupport", + "(Landroid/media/AudioFormat;Landroid/media/AudioAttributes;)I", + android_media_AudioSystem_getDirectPlaybackSupport), + MAKE_JNI_NATIVE_METHOD("getDirectProfilesForAttributes", + "(Landroid/media/AudioAttributes;Ljava/util/ArrayList;)I", + android_media_AudioSystem_getDirectProfilesForAttributes), + MAKE_JNI_NATIVE_METHOD("getSupportedMixerAttributes", "(ILjava/util/List;)I", + android_media_AudioSystem_getSupportedMixerAttributes), + MAKE_JNI_NATIVE_METHOD("setPreferredMixerAttributes", + "(Landroid/media/AudioAttributes;IILandroid/media/" + "AudioMixerAttributes;)I", + android_media_AudioSystem_setPreferredMixerAttributes), + MAKE_JNI_NATIVE_METHOD("getPreferredMixerAttributes", + "(Landroid/media/AudioAttributes;ILjava/util/List;)I", + android_media_AudioSystem_getPreferredMixerAttributes), + MAKE_JNI_NATIVE_METHOD("clearPreferredMixerAttributes", + "(Landroid/media/AudioAttributes;II)I", + android_media_AudioSystem_clearPreferredMixerAttributes), + MAKE_AUDIO_SYSTEM_METHOD(supportsBluetoothVariableLatency), + MAKE_AUDIO_SYSTEM_METHOD(setBluetoothVariableLatencyEnabled), + MAKE_AUDIO_SYSTEM_METHOD(isBluetoothVariableLatencyEnabled)}; + +static const JNINativeMethod gEventHandlerMethods[] = + {MAKE_JNI_NATIVE_METHOD("native_setup", "(Ljava/lang/Object;)V", + android_media_AudioSystem_eventHandlerSetup), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_finalize", + android_media_AudioSystem_eventHandlerFinalize)}; + +static const JNINativeMethod gFrameworkCapabilities[] = + {MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_getMaxChannelCount", + android_media_AudioSystem_getMaxChannelCount), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_getMaxSampleRate", + android_media_AudioSystem_getMaxSampleRate), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_getMinSampleRate", + android_media_AudioSystem_getMinSampleRate)}; int register_android_media_AudioSystem(JNIEnv *env) { @@ -3589,7 +3459,7 @@ int register_android_media_AudioSystem(JNIEnv *env) gClsAudioTrackRoutingProxy = android::FindClassOrDie(env, "android/media/AudioTrackRoutingProxy"); // make sure this reference doesn't get deleted - gClsAudioTrackRoutingProxy = (jclass)env->NewGlobalRef(gClsAudioTrackRoutingProxy); + gClsAudioTrackRoutingProxy = static_cast<jclass>(env->NewGlobalRef(gClsAudioTrackRoutingProxy)); gMidAudioTrackRoutingProxy_ctor = android::GetMethodIDOrDie(env, gClsAudioTrackRoutingProxy, "<init>", "(J)V"); @@ -3600,7 +3470,8 @@ int register_android_media_AudioSystem(JNIEnv *env) gClsAudioRecordRoutingProxy = android::FindClassOrDie(env, "android/media/AudioRecordRoutingProxy"); // make sure this reference doesn't get deleted - gClsAudioRecordRoutingProxy = (jclass)env->NewGlobalRef(gClsAudioRecordRoutingProxy); + gClsAudioRecordRoutingProxy = + static_cast<jclass>(env->NewGlobalRef(gClsAudioRecordRoutingProxy)); gMidAudioRecordRoutingProxy_ctor = android::GetMethodIDOrDie(env, gClsAudioRecordRoutingProxy, "<init>", "(J)V"); diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index ef51217a0805..3b36c7a6402b 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -5593,13 +5593,13 @@ <!-- Blur radius for the Option 3 in R.integer.config_letterboxBackgroundType. Values < 0 are ignored and 0 is used. --> - <dimen name="config_letterboxBackgroundWallpaperBlurRadius">31dp</dimen> + <dimen name="config_letterboxBackgroundWallpaperBlurRadius">24dp</dimen> <!-- Alpha of a black translucent scrim showed over wallpaper letterbox background when the Option 3 is selected for R.integer.config_letterboxBackgroundType. Values < 0 or >= 1 are ignored and 0.0 (transparent) is used instead. --> <item name="config_letterboxBackgroundWallaperDarkScrimAlpha" format="float" type="dimen"> - 0.5 + 0.68 </item> <!-- Corners appearance of the letterbox background. @@ -5624,7 +5624,7 @@ but isn't supported on the device or both dark scrim alpha and blur radius aren't provided. --> - <color name="config_letterboxBackgroundColor">@color/letterbox_background</color> + <color name="config_letterboxBackgroundColor">@color/system_on_secondary_fixed</color> <!-- Horizontal position of a center of the letterboxed app window. 0 corresponds to the left side of the screen and 1 to the right side. If given value < 0 diff --git a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java index 3c476300cf6b..33e81c163b4e 100644 --- a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java +++ b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java @@ -97,8 +97,7 @@ public class PasswordMetricsTest { @Test public void testComputeForPassword_metrics() { - final PasswordMetrics metrics = PasswordMetrics.computeForPasswordOrPin( - "6B~0z1Z3*8A".getBytes(), /* isPin */ false); + final PasswordMetrics metrics = metricsForPassword("6B~0z1Z3*8A"); assertEquals(11, metrics.length); assertEquals(4, metrics.letters); assertEquals(3, metrics.upperCase); @@ -136,72 +135,54 @@ public class PasswordMetricsTest { @Test public void testDetermineComplexity_lowNumeric() { - assertEquals(PASSWORD_COMPLEXITY_LOW, - PasswordMetrics.computeForPasswordOrPin("1234".getBytes(), - /* isPin */true).determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_LOW, metricsForPin("1234").determineComplexity()); } @Test public void testDetermineComplexity_lowNumericComplex() { - assertEquals(PASSWORD_COMPLEXITY_LOW, - PasswordMetrics.computeForPasswordOrPin("124".getBytes(), - /* isPin */ true).determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_LOW, metricsForPin("124").determineComplexity()); } @Test public void testDetermineComplexity_lowAlphabetic() { - assertEquals(PASSWORD_COMPLEXITY_LOW, - PasswordMetrics.computeForPasswordOrPin("a!".getBytes(), - /* isPin */ false).determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_LOW, metricsForPassword("a!").determineComplexity()); } @Test public void testDetermineComplexity_lowAlphanumeric() { - assertEquals(PASSWORD_COMPLEXITY_LOW, - PasswordMetrics.computeForPasswordOrPin("a!1".getBytes(), - /* isPin */ false).determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_LOW, metricsForPassword("a!1").determineComplexity()); } @Test public void testDetermineComplexity_mediumNumericComplex() { - assertEquals(PASSWORD_COMPLEXITY_MEDIUM, - PasswordMetrics.computeForPasswordOrPin("1238".getBytes(), - /* isPin */ true).determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_MEDIUM, metricsForPin("1238").determineComplexity()); } @Test public void testDetermineComplexity_mediumAlphabetic() { - assertEquals(PASSWORD_COMPLEXITY_MEDIUM, - PasswordMetrics.computeForPasswordOrPin("ab!c".getBytes(), - /* isPin */ false).determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_MEDIUM, metricsForPassword("ab!c").determineComplexity()); } @Test public void testDetermineComplexity_mediumAlphanumeric() { - assertEquals(PASSWORD_COMPLEXITY_MEDIUM, - PasswordMetrics.computeForPasswordOrPin("ab!1".getBytes(), - /* isPin */ false).determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_MEDIUM, metricsForPassword("ab!1").determineComplexity()); } @Test public void testDetermineComplexity_highNumericComplex() { - assertEquals(PASSWORD_COMPLEXITY_HIGH, - PasswordMetrics.computeForPasswordOrPin("12389647!".getBytes(), - /* isPin */ true).determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_HIGH, metricsForPin("12389647!").determineComplexity()); } @Test public void testDetermineComplexity_highAlphabetic() { assertEquals(PASSWORD_COMPLEXITY_HIGH, - PasswordMetrics.computeForPasswordOrPin("alphabetic!".getBytes(), - /* isPin */ false).determineComplexity()); + metricsForPassword("alphabetic!").determineComplexity()); } @Test public void testDetermineComplexity_highAlphanumeric() { assertEquals(PASSWORD_COMPLEXITY_HIGH, - PasswordMetrics.computeForPasswordOrPin("alphanumeric123!".getBytes(), - /* isPin */ false).determineComplexity()); + metricsForPassword("alphanumeric123!").determineComplexity()); } @Test @@ -425,6 +406,14 @@ public class PasswordMetricsTest { patternString.getBytes())); } + private static PasswordMetrics metricsForPassword(String password) { + return PasswordMetrics.computeForCredential(LockscreenCredential.createPassword(password)); + } + + private static PasswordMetrics metricsForPin(String pin) { + return PasswordMetrics.computeForCredential(LockscreenCredential.createPin(pin)); + } + @Test public void testValidateCredential_pattern() { PasswordMetrics adminMetrics = new PasswordMetrics(CREDENTIAL_TYPE_NONE); diff --git a/core/tests/coretests/src/android/view/ViewAttachTest.java b/core/tests/coretests/src/android/view/ViewAttachTest.java index 1a8dd9909b03..1da724a06d71 100644 --- a/core/tests/coretests/src/android/view/ViewAttachTest.java +++ b/core/tests/coretests/src/android/view/ViewAttachTest.java @@ -92,4 +92,43 @@ public class ViewAttachTest extends assertFalse(shouldDrawRoundScrollbars); } } + + /** + * Make sure that on any attached view, if the view is full-screen and hosted + * on a round device, the round scrollbars will be displayed even if the activity + * window is offset. + * + * @throws Throwable + */ + @UiThreadTest + public void testRoundScrollbarsWithMargins() throws Throwable { + final ViewAttachTestActivity activity = getActivity(); + final View rootView = activity.getWindow().getDecorView(); + final WindowManager.LayoutParams params = + new WindowManager.LayoutParams( + rootView.getWidth(), + rootView.getHeight(), + 50, /* xPosition */ + 0, /* yPosition */ + WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, + PixelFormat.TRANSLUCENT); + + rootView.setLayoutParams(params); + + // Configure margins to make sure they don't cause issues configuring rounded scrollbars. + final ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(1, 1); + lp.setMargins(1, 2, 3, 4); + rootView.setLayoutParams(lp); + + View contentView = activity.findViewById(R.id.view_attach_view); + boolean shouldDrawRoundScrollbars = contentView.shouldDrawRoundScrollbar(); + + if (activity.getResources().getConfiguration().isScreenRound()) { + assertTrue(shouldDrawRoundScrollbars); + } else { + // Never draw round scrollbars on non-round devices. + assertFalse(shouldDrawRoundScrollbars); + } + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java index 832131c3462b..7a48838ced91 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java @@ -66,11 +66,14 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; /** InfoMediaManager provide interface to get InfoMediaDevice list. */ @RequiresApi(Build.VERSION_CODES.R) @@ -693,25 +696,62 @@ public abstract class InfoMediaManager extends MediaManager { return filteredInfos; } + /** + * Returns an ordered list of available devices based on the provided {@code + * routeListingPreferenceItems}. + * + * <p>The result has the following order: + * + * <ol> + * <li>Selected routes. + * <li>Not-selected system routes. + * <li>Not-selected, non-system, available routes sorted by route listing preference. + * </ol> + * + * @param selectedRoutes List of currently selected routes. + * @param availableRoutes List of available routes that match the app's requested route + * features. + * @param routeListingPreferenceItems Ordered list of {@link RouteListingPreference.Item} to + * sort routes with. + */ @DoNotInline static List<MediaRoute2Info> arrangeRouteListByPreference( - List<MediaRoute2Info> selectedRouteInfos, List<MediaRoute2Info> infolist, - List<RouteListingPreference.Item> preferenceRouteListing) { - final List<MediaRoute2Info> sortedInfoList = new ArrayList<>(selectedRouteInfos); - infolist.removeAll(selectedRouteInfos); - sortedInfoList.addAll(infolist.stream().filter( - MediaRoute2Info::isSystemRoute).collect(Collectors.toList())); - for (RouteListingPreference.Item item : preferenceRouteListing) { - for (MediaRoute2Info info : infolist) { - if (item.getRouteId().equals(info.getId()) - && !selectedRouteInfos.contains(info) - && !info.isSystemRoute()) { - sortedInfoList.add(info); - break; - } + List<MediaRoute2Info> selectedRoutes, + List<MediaRoute2Info> availableRoutes, + List<RouteListingPreference.Item> routeListingPreferenceItems) { + Set<String> sortedRouteIds = new LinkedHashSet<>(); + + // Add selected routes first. + for (MediaRoute2Info selectedRoute : selectedRoutes) { + sortedRouteIds.add(selectedRoute.getId()); + } + + // Add not-yet-added system routes. + for (MediaRoute2Info availableRoute : availableRoutes) { + if (availableRoute.isSystemRoute()) { + sortedRouteIds.add(availableRoute.getId()); + } + } + + // Create a mapping from id to route to avoid a quadratic search. + Map<String, MediaRoute2Info> idToRouteMap = + Stream.concat(selectedRoutes.stream(), availableRoutes.stream()) + .collect( + Collectors.toMap( + MediaRoute2Info::getId, + Function.identity(), + (route1, route2) -> route1)); + + // Add not-selected routes that match RLP items. All system routes have already been + // added at this point. + for (RouteListingPreference.Item item : routeListingPreferenceItems) { + MediaRoute2Info route = idToRouteMap.get(item.getRouteId()); + if (route != null) { + sortedRouteIds.add(route.getId()); } } - return sortedInfoList; + + return sortedRouteIds.stream().map(idToRouteMap::get).collect(Collectors.toList()); } @DoNotInline diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 4633a659fcb7..7be60431b91b 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -123,6 +123,7 @@ android_library { ], static_libs: [ "SystemUISharedLib", + "SystemUICustomizationLib", "SettingsLib", "androidx.leanback_leanback", "androidx.slice_slice-core", diff --git a/packages/SystemUI/customization/res/values-h800dp/dimens.xml b/packages/SystemUI/customization/res/values-h800dp/dimens.xml index 60afc8a97a71..cb4994516a3a 100644 --- a/packages/SystemUI/customization/res/values-h800dp/dimens.xml +++ b/packages/SystemUI/customization/res/values-h800dp/dimens.xml @@ -17,4 +17,7 @@ <resources> <!-- Large clock maximum font size (dp is intentional, to prevent any further scaling) --> <dimen name="large_clock_text_size">200dp</dimen> + + <!-- With the large clock, move up slightly from the center --> + <dimen name="keyguard_large_clock_top_margin">-112dp</dimen> </resources> diff --git a/packages/SystemUI/customization/res/values/dimens.xml b/packages/SystemUI/customization/res/values/dimens.xml index ba8f2843cdfc..8eb8132b07b9 100644 --- a/packages/SystemUI/customization/res/values/dimens.xml +++ b/packages/SystemUI/customization/res/values/dimens.xml @@ -24,4 +24,12 @@ <item name="keyguard_clock_line_spacing_scale" type="dimen" format="float">.7</item> <!-- Burmese line spacing multiplier between hours and minutes of the keyguard clock --> <item name="keyguard_clock_line_spacing_scale_burmese" type="dimen" format="float">1</item> + + <!-- With the large clock, move up slightly from the center --> + <dimen name="keyguard_large_clock_top_margin">-60dp</dimen> + + <!-- additional offset for clock switch area items --> + <dimen name="small_clock_height">114dp</dimen> + <dimen name="small_clock_padding_top">28dp</dimen> + <dimen name="clock_padding_start">28dp</dimen> </resources>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml index 39dd90e5ed60..8c817330953c 100644 --- a/packages/SystemUI/res-keyguard/values/dimens.xml +++ b/packages/SystemUI/res-keyguard/values/dimens.xml @@ -95,9 +95,6 @@ <dimen name="num_pad_key_margin_end">12dp</dimen> <!-- additional offset for clock switch area items --> - <dimen name="small_clock_height">114dp</dimen> - <dimen name="small_clock_padding_top">28dp</dimen> - <dimen name="clock_padding_start">28dp</dimen> <dimen name="below_clock_padding_start">32dp</dimen> <dimen name="below_clock_padding_end">16dp</dimen> <dimen name="below_clock_padding_start_icons">28dp</dimen> diff --git a/core/res/res/color/letterbox_background.xml b/packages/SystemUI/res/color/brightness_slider_overlay_color.xml index 955948ad2b6a..a8abd793bd00 100644 --- a/core/res/res/color/letterbox_background.xml +++ b/packages/SystemUI/res/color/brightness_slider_overlay_color.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2022 The Android Open Source Project + ~ Copyright (C) 2023 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. @@ -14,6 +14,9 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> + <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:color="@color/system_neutral1_500" android:lStar="5" /> -</selector> + <item android:state_pressed="true" android:color="?attr/onShadeActive" android:alpha="0.12" /> + <item android:state_hovered="true" android:color="?attr/onShadeActive" android:alpha="0.09" /> + <item android:color="@color/transparent" /> +</selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml index 2ea90c717863..a9e7adf668a9 100644 --- a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml +++ b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml @@ -26,6 +26,13 @@ <corners android:radius="@dimen/rounded_slider_corner_radius"/> </shape> </item> + <item> + <shape> + <corners android:radius="@dimen/rounded_slider_corner_radius" /> + <size android:height="@dimen/rounded_slider_height" /> + <solid android:color="@color/brightness_slider_overlay_color" /> + </shape> + </item> <item android:id="@+id/slider_icon" android:gravity="center_vertical|right" diff --git a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml index 87b5a4c2fc5b..32dc4b335f7e 100644 --- a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml +++ b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml @@ -20,7 +20,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:shape="rectangle"> - <solid android:color="?androidprv:attr/colorSurface"/> + <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh"/> <size android:width="@dimen/keyguard_affordance_fixed_width" android:height="@dimen/keyguard_affordance_fixed_height"/> diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml index 665c6127e06d..f3a6bbeaaf0e 100644 --- a/packages/SystemUI/res/layout/combined_qs_header.xml +++ b/packages/SystemUI/res/layout/combined_qs_header.xml @@ -121,10 +121,12 @@ frame when animating QS <-> QQS transition <LinearLayout android:id="@+id/shade_header_system_icons" android:layout_width="wrap_content" - app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height" - android:layout_height="@dimen/large_screen_shade_header_min_height" + android:layout_height="@dimen/shade_header_system_icons_height" android:clickable="true" android:orientation="horizontal" + android:gravity="center_vertical" + android:paddingStart="@dimen/shade_header_system_icons_padding_start" + android:paddingEnd="@dimen/shade_header_system_icons_padding_end" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="@id/privacy_container" app:layout_constraintTop_toTopOf="@id/clock"> @@ -132,13 +134,13 @@ frame when animating QS <-> QQS transition <com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons" android:layout_width="wrap_content" - android:layout_height="match_parent" + android:layout_height="wrap_content" android:paddingEnd="@dimen/signal_cluster_battery_padding" /> <com.android.systemui.battery.BatteryMeterView android:id="@+id/batteryRemainingIcon" android:layout_width="wrap_content" - android:layout_height="match_parent" + android:layout_height="wrap_content" app:textAppearance="@style/TextAppearance.QS.Status" /> </LinearLayout> diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml index e95c6a79733c..91550b3dcac0 100644 --- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml +++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml @@ -34,5 +34,6 @@ android:paddingEnd="0dp" android:progressDrawable="@drawable/brightness_progress_drawable" android:splitTrack="false" + android:clickable="true" /> </com.android.systemui.settings.brightness.BrightnessSliderView> diff --git a/packages/SystemUI/res/values-h800dp/dimens.xml b/packages/SystemUI/res/values-h800dp/dimens.xml index 3a71994e07e2..829ef98956c5 100644 --- a/packages/SystemUI/res/values-h800dp/dimens.xml +++ b/packages/SystemUI/res/values-h800dp/dimens.xml @@ -15,9 +15,6 @@ --> <resources> - <!-- With the large clock, move up slightly from the center --> - <dimen name="keyguard_large_clock_top_margin">-112dp</dimen> - <!-- Margin above the ambient indication container --> <dimen name="ambient_indication_container_margin_top">20dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml index 65a08b9a3726..2b1d9d6c2f10 100644 --- a/packages/SystemUI/res/values-sw600dp/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp/dimens.xml @@ -77,6 +77,9 @@ <dimen name="large_dialog_width">472dp</dimen> <dimen name="large_screen_shade_header_height">42dp</dimen> + <!-- start padding is smaller to account for status icon margins coming from drawable itself --> + <dimen name="shade_header_system_icons_padding_start">11dp</dimen> + <dimen name="shade_header_system_icons_padding_end">12dp</dimen> <!-- Lockscreen shade transition values --> <dimen name="lockscreen_shade_transition_by_tap_distance">200dp</dimen> diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml index 28f98f4821fa..de913ace8fda 100644 --- a/packages/SystemUI/res/values-sw720dp/dimens.xml +++ b/packages/SystemUI/res/values-sw720dp/dimens.xml @@ -27,6 +27,9 @@ <dimen name="large_screen_shade_header_height">56dp</dimen> + <!-- it's a bit smaller on 720dp to account for status_bar_icon_horizontal_margin --> + <dimen name="shade_header_system_icons_padding_start">10dp</dimen> + <!-- Biometric Auth pattern view size, better to align keyguard_security_width --> <dimen name="biometric_auth_pattern_view_size">348dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 1f9de7a5c4d4..47cd1e707557 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -482,6 +482,10 @@ <dimen name="large_screen_shade_header_height">48dp</dimen> <dimen name="large_screen_shade_header_min_height">@dimen/qs_header_row_min_height</dimen> <dimen name="large_screen_shade_header_left_padding">@dimen/qs_horizontal_margin</dimen> + <dimen name="shade_header_system_icons_height">@dimen/large_screen_shade_header_min_height</dimen> + <dimen name="shade_header_system_icons_height_large_screen">32dp</dimen> + <dimen name="shade_header_system_icons_padding_start">0dp</dimen> + <dimen name="shade_header_system_icons_padding_end">0dp</dimen> <!-- The top margin of the panel that holds the list of notifications. On phones it's always 0dp but it's overridden in Car UI @@ -743,8 +747,6 @@ <dimen name="keyguard_clock_switch_y_shift">14dp</dimen> <!-- When large clock is showing, offset the smartspace by this amount --> <dimen name="keyguard_smartspace_top_offset">12dp</dimen> - <!-- With the large clock, move up slightly from the center --> - <dimen name="keyguard_large_clock_top_margin">-60dp</dimen> <dimen name="notification_scrim_corner_radius">32dp</dimen> diff --git a/packages/SystemUI/res/xml/large_screen_shade_header.xml b/packages/SystemUI/res/xml/large_screen_shade_header.xml index 39f4c81b6dbe..cb2c3a19a6d5 100644 --- a/packages/SystemUI/res/xml/large_screen_shade_header.xml +++ b/packages/SystemUI/res/xml/large_screen_shade_header.xml @@ -56,7 +56,7 @@ <Constraint android:id="@+id/shade_header_system_icons"> <Layout android:layout_width="wrap_content" - android:layout_height="@dimen/large_screen_shade_header_min_height" + android:layout_height="@dimen/shade_header_system_icons_height_large_screen" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/privacy_container" app:layout_constraintTop_toTopOf="parent" diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricModality.kt b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/BiometricModality.kt index 3197c0935d0b..fb580ca54aff 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricModality.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/BiometricModality.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.biometrics.domain.model +package com.android.systemui.biometrics.shared.model import android.hardware.biometrics.BiometricAuthenticator diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt index 64df6a03001d..e5a4d1a644f1 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt @@ -46,9 +46,9 @@ import com.android.systemui.biometrics.AuthIconController import com.android.systemui.biometrics.AuthPanelController import com.android.systemui.biometrics.Utils import com.android.systemui.biometrics.domain.model.BiometricModalities -import com.android.systemui.biometrics.domain.model.BiometricModality -import com.android.systemui.biometrics.domain.model.asBiometricModality +import com.android.systemui.biometrics.shared.model.BiometricModality import com.android.systemui.biometrics.shared.model.PromptKind +import com.android.systemui.biometrics.shared.model.asBiometricModality import com.android.systemui.biometrics.ui.BiometricPromptLayout import com.android.systemui.biometrics.ui.viewmodel.FingerprintStartMode import com.android.systemui.biometrics.ui.viewmodel.PromptMessage @@ -396,7 +396,6 @@ private class Spaghetti( private var lifecycleScope: CoroutineScope? = null private var modalities: BiometricModalities = BiometricModalities() - private var faceFailedAtLeastOnce = false private var legacyCallback: Callback? = null override var legacyIconController: AuthIconController? = null @@ -476,19 +475,15 @@ private class Spaghetti( viewModel.ensureFingerprintHasStarted(isDelayed = true) applicationScope.launch { - val suppress = - modalities.hasFaceAndFingerprint && - (failedModality == BiometricModality.Face) && - faceFailedAtLeastOnce - if (failedModality == BiometricModality.Face) { - faceFailedAtLeastOnce = true - } - viewModel.showTemporaryError( failureReason, messageAfterError = modalities.asDefaultHelpMessage(applicationContext), authenticateAfterError = modalities.hasFingerprint, - suppressIfErrorShowing = suppress, + suppressIf = { currentMessage -> + modalities.hasFaceAndFingerprint && + failedModality == BiometricModality.Face && + currentMessage.isError + }, failedModality = failedModality, ) } @@ -501,11 +496,10 @@ private class Spaghetti( } applicationScope.launch { - val suppress = - modalities.hasFaceAndFingerprint && (errorModality == BiometricModality.Face) viewModel.showTemporaryError( error, - suppressIfErrorShowing = suppress, + messageAfterError = modalities.asDefaultHelpMessage(applicationContext), + authenticateAfterError = modalities.hasFingerprint, ) delay(BiometricPrompt.HIDE_DIALOG_DELAY.toLong()) legacyCallback?.onAction(Callback.ACTION_ERROR) @@ -522,6 +516,7 @@ private class Spaghetti( viewModel.showTemporaryError( help, messageAfterError = modalities.asDefaultHelpMessage(applicationContext), + authenticateAfterError = modalities.hasFingerprint, hapticFeedback = false, ) } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthState.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthState.kt index 444082ca2742..2f9557f70a32 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthState.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthState.kt @@ -16,7 +16,7 @@ package com.android.systemui.biometrics.ui.viewmodel -import com.android.systemui.biometrics.domain.model.BiometricModality +import com.android.systemui.biometrics.shared.model.BiometricModality /** * The authenticated state with the [authenticatedModality] (when [isAuthenticated]) with an diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptMessage.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptMessage.kt index 219da716f7d9..50f491142949 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptMessage.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptMessage.kt @@ -33,9 +33,9 @@ sealed interface PromptMessage { else -> "" } - /** If this is an [Error] or [Help] message. */ - val isErrorOrHelp: Boolean - get() = this is Error || this is Help + /** If this is an [Error]. */ + val isError: Boolean + get() = this is Error /** An error message. */ data class Error(val errorMessage: String) : PromptMessage diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt index d63bf57013e5..8a2e4059ee73 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt @@ -20,7 +20,7 @@ import android.util.Log import com.android.systemui.biometrics.AuthBiometricView import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor import com.android.systemui.biometrics.domain.model.BiometricModalities -import com.android.systemui.biometrics.domain.model.BiometricModality +import com.android.systemui.biometrics.shared.model.BiometricModality import com.android.systemui.biometrics.shared.model.PromptKind import com.android.systemui.statusbar.VibratorHelper import javax.inject.Inject @@ -210,35 +210,33 @@ constructor( * Show a temporary error [message] associated with an optional [failedModality] and play * [hapticFeedback]. * - * An optional [messageAfterError] will be shown via [showAuthenticating] when - * [authenticateAfterError] is set (or via [showHelp] when not set) after the error is - * dismissed. + * The [messageAfterError] will be shown via [showAuthenticating] when [authenticateAfterError] + * is set (or via [showHelp] when not set) after the error is dismissed. * - * The error is ignored if the user has already authenticated or if [suppressIfErrorShowing] is - * set and an error message is already showing. + * The error is ignored if the user has already authenticated or if [suppressIf] is true given + * the currently showing [PromptMessage]. */ suspend fun showTemporaryError( message: String, + messageAfterError: String, + authenticateAfterError: Boolean, + suppressIf: (PromptMessage) -> Boolean = { false }, hapticFeedback: Boolean = true, - messageAfterError: String = "", - authenticateAfterError: Boolean = false, - suppressIfErrorShowing: Boolean = false, failedModality: BiometricModality = BiometricModality.None, ) = coroutineScope { if (_isAuthenticated.value.isAuthenticated) { return@coroutineScope } - if (_message.value.isErrorOrHelp && suppressIfErrorShowing) { - if (_isAuthenticated.value.isNotAuthenticated) { - _canTryAgainNow.value = supportsRetry(failedModality) - } + + _canTryAgainNow.value = supportsRetry(failedModality) + + if (suppressIf(_message.value)) { return@coroutineScope } _isAuthenticating.value = false _isAuthenticated.value = PromptAuthState(false) _forceMediumSize.value = true - _canTryAgainNow.value = supportsRetry(failedModality) _message.value = PromptMessage.Error(message) _legacyState.value = AuthBiometricView.STATE_ERROR @@ -374,7 +372,9 @@ constructor( AuthBiometricView.STATE_AUTHENTICATED } - vibrator.success(modality) + if (!needsUserConfirmation) { + vibrator.success(modality) + } messageJob?.cancel() messageJob = null @@ -420,6 +420,8 @@ constructor( _message.value = PromptMessage.Empty _legacyState.value = AuthBiometricView.STATE_AUTHENTICATED + vibrator.success(authState.authenticatedModality) + messageJob?.cancel() messageJob = null } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt index 2513c81c17f8..62a484d42dec 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt @@ -25,6 +25,8 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.bouncer.data.repository.BouncerRepository import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel @@ -38,9 +40,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -53,6 +52,7 @@ constructor( private val repository: BouncerRepository, private val authenticationInteractor: AuthenticationInteractor, private val sceneInteractor: SceneInteractor, + featureFlags: FeatureFlags, @Assisted private val containerName: String, ) { @@ -95,36 +95,14 @@ constructor( val isPatternVisible: StateFlow<Boolean> = authenticationInteractor.isPatternVisible init { - // UNLOCKING SHOWS Gone. - // - // Move to the gone scene if the device becomes unlocked while on the bouncer scene. - applicationScope.launch { - sceneInteractor - .currentScene(containerName) - .flatMapLatest { currentScene -> - if (currentScene.key == SceneKey.Bouncer) { - authenticationInteractor.isUnlocked - } else { - flowOf(false) - } - } - .distinctUntilChanged() - .collect { isUnlocked -> - if (isUnlocked) { - sceneInteractor.setCurrentScene( - containerName = containerName, - scene = SceneModel(SceneKey.Gone), - ) + if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { + // Clear the message if moved from throttling to no-longer throttling. + applicationScope.launch { + isThrottled.pairwise().collect { (wasThrottled, currentlyThrottled) -> + if (wasThrottled && !currentlyThrottled) { + clearMessage() } } - } - - // Clear the message if moved from throttling to no-longer throttling. - applicationScope.launch { - isThrottled.pairwise().collect { (wasThrottled, currentlyThrottled) -> - if (wasThrottled && !currentlyThrottled) { - clearMessage() - } } } } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt index 5425022e11b7..a4ef5cec6525 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt @@ -23,6 +23,8 @@ import com.android.systemui.R import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.util.kotlin.pairwise import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -49,6 +51,7 @@ constructor( @Application private val applicationContext: Context, @Application private val applicationScope: CoroutineScope, interactorFactory: BouncerInteractor.Factory, + featureFlags: FeatureFlags, @Assisted containerName: String, ) { private val interactor: BouncerInteractor = interactorFactory.create(containerName) @@ -102,15 +105,48 @@ constructor( ) init { - applicationScope.launch { - _authMethod.subscriptionCount - .pairwise() - .map { (previousCount, currentCount) -> currentCount > previousCount } - .collect { subscriberAdded -> - if (subscriberAdded) { - reloadAuthMethod() + if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { + applicationScope.launch { + interactor.isThrottled + .map { isThrottled -> + if (isThrottled) { + when (interactor.getAuthenticationMethod()) { + is AuthenticationMethodModel.Pin -> + R.string.kg_too_many_failed_pin_attempts_dialog_message + is AuthenticationMethodModel.Password -> + R.string.kg_too_many_failed_password_attempts_dialog_message + is AuthenticationMethodModel.Pattern -> + R.string.kg_too_many_failed_pattern_attempts_dialog_message + else -> null + }?.let { stringResourceId -> + applicationContext.getString( + stringResourceId, + interactor.throttling.value.failedAttemptCount, + ceil(interactor.throttling.value.remainingMs / 1000f).toInt(), + ) + } + } else { + null + } + } + .distinctUntilChanged() + .collect { dialogMessageOrNull -> + if (dialogMessageOrNull != null) { + _throttlingDialogMessage.value = dialogMessageOrNull + } + } + } + + applicationScope.launch { + _authMethod.subscriptionCount + .pairwise() + .map { (previousCount, currentCount) -> currentCount > previousCount } + .collect { subscriberAdded -> + if (subscriberAdded) { + reloadAuthMethod() + } } - } + } } } @@ -144,39 +180,6 @@ constructor( */ val throttlingDialogMessage: StateFlow<String?> = _throttlingDialogMessage.asStateFlow() - init { - applicationScope.launch { - interactor.isThrottled - .map { isThrottled -> - if (isThrottled) { - when (interactor.getAuthenticationMethod()) { - is AuthenticationMethodModel.Pin -> - R.string.kg_too_many_failed_pin_attempts_dialog_message - is AuthenticationMethodModel.Password -> - R.string.kg_too_many_failed_password_attempts_dialog_message - is AuthenticationMethodModel.Pattern -> - R.string.kg_too_many_failed_pattern_attempts_dialog_message - else -> null - }?.let { stringResourceId -> - applicationContext.getString( - stringResourceId, - interactor.throttling.value.failedAttemptCount, - ceil(interactor.throttling.value.remainingMs / 1000f).toInt(), - ) - } - } else { - null - } - } - .distinctUntilChanged() - .collect { dialogMessageOrNull -> - if (dialogMessageOrNull != null) { - _throttlingDialogMessage.value = dialogMessageOrNull - } - } - } - } - /** Notifies that the emergency services button was clicked. */ fun onEmergencyServicesButtonClicked() { // TODO(b/280877228): implement this diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java index 5493cea8fd02..94b2ab113527 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java @@ -19,6 +19,7 @@ package com.android.systemui.dagger; import com.android.systemui.globalactions.ShutdownUiModule; import com.android.systemui.keyguard.CustomizationProvider; import com.android.systemui.scene.startable.SceneContainerStartableModule; +import com.android.systemui.shade.ShadeModule; import com.android.systemui.statusbar.NotificationInsetsModule; import com.android.systemui.statusbar.QsFrameTranslateModule; @@ -33,6 +34,7 @@ import dagger.Subcomponent; DependencyProvider.class, NotificationInsetsModule.class, QsFrameTranslateModule.class, + ShadeModule.class, ShutdownUiModule.class, SceneContainerStartableModule.class, SystemUIBinder.class, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index eef850821166..18b56125a93e 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -81,7 +81,6 @@ import com.android.systemui.screenshot.dagger.ScreenshotModule; import com.android.systemui.security.data.repository.SecurityRepositoryModule; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.shade.ShadeController; -import com.android.systemui.shade.ShadeModule; import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; import com.android.systemui.shade.transition.LargeScreenShadeInterpolatorImpl; import com.android.systemui.shared.condition.Monitor; @@ -198,7 +197,6 @@ import javax.inject.Named; SecurityRepositoryModule.class, ScreenRecordModule.class, SettingsUtilModule.class, - ShadeModule.class, SmartRepliesInflationModule.class, SmartspaceModule.class, StatusBarPipelineModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 45a8f13311a0..a556572dac88 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -258,6 +258,16 @@ object Flags { @JvmField val MIGRATE_INDICATION_AREA = unreleasedFlag(236, "migrate_indication_area", teamfood = true) + /** + * Migrate the bottom area to the new keyguard root view. + * Because there is no such thing as a "bottom area" after this, this also breaks it up into + * many smaller, modular pieces. + */ + // TODO(b/290652751): Tracking bug. + @JvmField + val MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA = + unreleasedFlag(290652751, "migrate_split_keyguard_bottom_area") + /** Whether to listen for fingerprint authentication over keyguard occluding activities. */ // TODO(b/283260512): Tracking bug. @JvmField @@ -277,6 +287,10 @@ object Flags { @JvmField val MIGRATE_LOCK_ICON = unreleasedFlag(240, "migrate_lock_icon") + // TODO(b/288276738): Tracking bug. + @JvmField + val WIDGET_ON_KEYGUARD = unreleasedFlag(241, "widget_on_keyguard") + // 300 - power menu // TODO(b/254512600): Tracking Bug @JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite") diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt index c8f7efbeb397..1c200b086990 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt @@ -20,20 +20,14 @@ import com.android.systemui.authentication.domain.interactor.AuthenticationInter import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.scene.domain.interactor.SceneInteractor -import com.android.systemui.scene.shared.model.SceneKey -import com.android.systemui.scene.shared.model.SceneModel import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch /** Hosts business and application state accessing logic for the lockscreen scene. */ class LockscreenSceneInteractor @@ -42,7 +36,6 @@ constructor( @Application applicationScope: CoroutineScope, private val authenticationInteractor: AuthenticationInteractor, bouncerInteractorFactory: BouncerInteractor.Factory, - private val sceneInteractor: SceneInteractor, @Assisted private val containerName: String, ) { private val bouncerInteractor: BouncerInteractor = @@ -72,46 +65,6 @@ constructor( initialValue = false, ) - init { - // LOCKING SHOWS Lockscreen. - // - // Move to the lockscreen scene if the device becomes locked while in any scene. - applicationScope.launch { - authenticationInteractor.isUnlocked - .map { !it } - .distinctUntilChanged() - .collect { isLocked -> - if (isLocked) { - sceneInteractor.setCurrentScene( - containerName = containerName, - scene = SceneModel(SceneKey.Lockscreen), - ) - } - } - } - - // BYPASS UNLOCK. - // - // Moves to the gone scene if bypass is enabled and the device becomes unlocked while in the - // lockscreen scene. - applicationScope.launch { - combine( - authenticationInteractor.isBypassEnabled, - authenticationInteractor.isUnlocked, - sceneInteractor.currentScene(containerName), - ::Triple, - ) - .collect { (isBypassEnabled, isUnlocked, currentScene) -> - if (isBypassEnabled && isUnlocked && currentScene.key == SceneKey.Lockscreen) { - sceneInteractor.setCurrentScene( - containerName = containerName, - scene = SceneModel(SceneKey.Gone), - ) - } - } - } - } - /** Attempts to dismiss the lockscreen. This will cause the bouncer to show, if needed. */ fun dismissLockscreen() { bouncerInteractor.showOrUnlockDevice(containerName = containerName) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt index 7d14198bdb17..db84268f7c58 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt @@ -343,9 +343,9 @@ object KeyguardBottomAreaViewBinder { Utils.getColorAttrDefaultColor( view.context, if (viewModel.isActivated) { - com.android.internal.R.attr.textColorPrimaryInverse + com.android.internal.R.attr.materialColorOnPrimaryFixed } else { - com.android.internal.R.attr.textColorPrimary + com.android.internal.R.attr.materialColorOnSurface }, ) ) @@ -355,9 +355,9 @@ object KeyguardBottomAreaViewBinder { Utils.getColorAttr( view.context, if (viewModel.isActivated) { - com.android.internal.R.attr.colorAccentPrimary + com.android.internal.R.attr.materialColorPrimaryFixed } else { - com.android.internal.R.attr.colorSurface + com.android.internal.R.attr.materialColorSurfaceContainerHigh } ) } else { diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt index 91400196bbb1..59f82f034723 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt @@ -17,6 +17,7 @@ package com.android.systemui.scene.domain.startable import com.android.systemui.CoreStartable +import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.flags.FeatureFlags @@ -24,9 +25,11 @@ import com.android.systemui.flags.Flags import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.SceneContainerNames import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.SceneModel import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch @@ -41,17 +44,19 @@ class SystemUiDefaultSceneContainerStartable constructor( @Application private val applicationScope: CoroutineScope, private val sceneInteractor: SceneInteractor, + private val authenticationInteractor: AuthenticationInteractor, private val featureFlags: FeatureFlags, ) : CoreStartable { override fun start() { if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { - keepVisibilityUpdated() + hydrateVisibility() + automaticallySwitchScenes() } } - /** Drives visibility of the scene container. */ - private fun keepVisibilityUpdated() { + /** Updates the visibility of the scene container based on the current scene. */ + private fun hydrateVisibility() { applicationScope.launch { sceneInteractor .currentScene(CONTAINER_NAME) @@ -63,6 +68,48 @@ constructor( } } + /** Switches between scenes based on ever-changing application state. */ + private fun automaticallySwitchScenes() { + applicationScope.launch { + authenticationInteractor.isUnlocked + .map { isUnlocked -> + val currentSceneKey = sceneInteractor.currentScene(CONTAINER_NAME).value.key + val isBypassEnabled = authenticationInteractor.isBypassEnabled.value + when { + isUnlocked -> + when (currentSceneKey) { + // When the device becomes unlocked in Bouncer, go to the Gone. + is SceneKey.Bouncer -> SceneKey.Gone + // When the device becomes unlocked in Lockscreen, go to Gone if + // bypass is enabled. + is SceneKey.Lockscreen -> SceneKey.Gone.takeIf { isBypassEnabled } + // We got unlocked while on a scene that's not Lockscreen or + // Bouncer, no need to change scenes. + else -> null + } + // When the device becomes locked, to Lockscreen. + !isUnlocked -> + when (currentSceneKey) { + // Already on lockscreen or bouncer, no need to change scenes. + is SceneKey.Lockscreen, + is SceneKey.Bouncer -> null + // We got locked while on a scene that's not Lockscreen or Bouncer, + // go to Lockscreen. + else -> SceneKey.Lockscreen + } + else -> null + } + } + .filterNotNull() + .collect { targetSceneKey -> + sceneInteractor.setCurrentScene( + containerName = CONTAINER_NAME, + scene = SceneModel(targetSceneKey), + ) + } + } + } + companion object { private const val CONTAINER_NAME = SceneContainerNames.SYSTEM_UI_DEFAULT } diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java index 5199bd43f982..182e4569b549 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java @@ -18,6 +18,7 @@ package com.android.systemui.settings.brightness; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; +import static android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY; import android.app.Activity; import android.graphics.Rect; @@ -29,8 +30,10 @@ import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; +import android.view.accessibility.AccessibilityManager; import android.widget.FrameLayout; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.R; @@ -38,34 +41,42 @@ import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserTracker; +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; +import com.android.systemui.util.concurrency.DelayableExecutor; import java.util.List; -import java.util.concurrent.Executor; import javax.inject.Inject; /** A dialog that provides controls for adjusting the screen brightness. */ public class BrightnessDialog extends Activity { + @VisibleForTesting + static final int DIALOG_TIMEOUT_MILLIS = 3000; + private BrightnessController mBrightnessController; private final BrightnessSliderController.Factory mToggleSliderFactory; private final UserTracker mUserTracker; private final DisplayTracker mDisplayTracker; - private final Executor mMainExecutor; + private final DelayableExecutor mMainExecutor; private final Handler mBackgroundHandler; + private final AccessibilityManagerWrapper mAccessibilityMgr; + private Runnable mCancelTimeoutRunnable; @Inject public BrightnessDialog( UserTracker userTracker, DisplayTracker displayTracker, BrightnessSliderController.Factory factory, - @Main Executor mainExecutor, - @Background Handler bgHandler) { + @Main DelayableExecutor mainExecutor, + @Background Handler bgHandler, + AccessibilityManagerWrapper accessibilityMgr) { mUserTracker = userTracker; mDisplayTracker = displayTracker; mToggleSliderFactory = factory; mMainExecutor = mainExecutor; mBackgroundHandler = bgHandler; + mAccessibilityMgr = accessibilityMgr; } @@ -122,6 +133,14 @@ public class BrightnessDialog extends Activity { } @Override + protected void onResume() { + super.onResume(); + if (triggeredByBrightnessKey()) { + scheduleTimeout(); + } + } + + @Override protected void onPause() { super.onPause(); overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); @@ -139,9 +158,25 @@ public class BrightnessDialog extends Activity { if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) { + if (mCancelTimeoutRunnable != null) { + mCancelTimeoutRunnable.run(); + } finish(); } return super.onKeyDown(keyCode, event); } + + private boolean triggeredByBrightnessKey() { + return getIntent().getBooleanExtra(EXTRA_FROM_BRIGHTNESS_KEY, false); + } + + private void scheduleTimeout() { + if (mCancelTimeoutRunnable != null) { + mCancelTimeoutRunnable.run(); + } + final int timeout = mAccessibilityMgr.getRecommendedTimeoutMillis(DIALOG_TIMEOUT_MILLIS, + AccessibilityManager.FLAG_CONTENT_CONTROLS); + mCancelTimeoutRunnable = mMainExecutor.executeDelayed(this::finish, timeout); + } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java index ebb98883c679..02f337a8752a 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java @@ -18,6 +18,7 @@ package com.android.systemui.shade; import android.view.MotionEvent; +import com.android.systemui.CoreStartable; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.StatusBarState; @@ -31,7 +32,7 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; * these are coordinated with {@link StatusBarKeyguardViewManager} via * {@link com.android.systemui.keyguard.KeyguardViewMediator} and others. */ -public interface ShadeController { +public interface ShadeController extends CoreStartable { /** Make our window larger and the shade expanded */ void instantExpandShade(); @@ -164,17 +165,14 @@ public interface ShadeController { void onLaunchAnimationEnd(boolean launchIsFullScreen); /** Sets the listener for when the visibility of the shade changes. */ - default void setVisibilityListener(ShadeVisibilityListener listener) {}; + default void setVisibilityListener(ShadeVisibilityListener listener) {} /** */ - default void setNotificationPresenter(NotificationPresenter presenter) {}; + default void setNotificationPresenter(NotificationPresenter presenter) {} /** */ default void setNotificationShadeWindowViewController( - NotificationShadeWindowViewController notificationShadeWindowViewController) {}; - - /** */ - default void setShadeViewController(ShadeViewController shadeViewController) {}; + NotificationShadeWindowViewController notificationShadeWindowViewController) {} /** Listens for shade visibility changes. */ interface ShadeVisibilityListener { diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt index 4d0500786ca3..a8ef8e904694 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt @@ -7,6 +7,7 @@ import javax.inject.Inject /** Empty implementation of ShadeController for variants of Android without shades. */ @SysUISingleton open class ShadeControllerEmptyImpl @Inject constructor() : ShadeController { + override fun start() {} override fun instantExpandShade() {} override fun instantCollapseShade() {} override fun animateCollapseShade( diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java index af74a8d7dca1..22c638177a48 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java @@ -63,6 +63,7 @@ public final class ShadeControllerImpl implements ShadeController { private final StatusBarWindowController mStatusBarWindowController; private final DeviceProvisionedController mDeviceProvisionedController; + private final Lazy<ShadeViewController> mShadeViewControllerLazy; private final Lazy<AssistManager> mAssistManagerLazy; private final Lazy<NotificationGutsManager> mGutsManager; @@ -70,8 +71,6 @@ public final class ShadeControllerImpl implements ShadeController { private boolean mExpandedVisible; - // TODO(b/237661616): Rename this variable to mShadeViewController. - private ShadeViewController mNotificationPanelViewController; private NotificationPresenter mPresenter; private NotificationShadeWindowViewController mNotificationShadeWindowViewController; private ShadeVisibilityListener mShadeVisibilityListener; @@ -87,11 +86,13 @@ public final class ShadeControllerImpl implements ShadeController { DeviceProvisionedController deviceProvisionedController, NotificationShadeWindowController notificationShadeWindowController, WindowManager windowManager, + Lazy<ShadeViewController> shadeViewControllerLazy, Lazy<AssistManager> assistManagerLazy, Lazy<NotificationGutsManager> gutsManager ) { mCommandQueue = commandQueue; mMainExecutor = mainExecutor; + mShadeViewControllerLazy = shadeViewControllerLazy; mStatusBarStateController = statusBarStateController; mStatusBarWindowController = statusBarWindowController; mDeviceProvisionedController = deviceProvisionedController; @@ -107,7 +108,7 @@ public final class ShadeControllerImpl implements ShadeController { public void instantExpandShade() { // Make our window larger and the panel expanded. makeExpandedVisible(true /* force */); - mNotificationPanelViewController.expand(false /* animate */); + getShadeViewController().expand(false /* animate */); mCommandQueue.recomputeDisableFlags(mDisplayId, false /* animate */); } @@ -123,13 +124,13 @@ public final class ShadeControllerImpl implements ShadeController { "animateCollapse(): mExpandedVisible=" + mExpandedVisible + "flags=" + flags); } if (getNotificationShadeWindowView() != null - && mNotificationPanelViewController.canBeCollapsed() + && getShadeViewController().canBeCollapsed() && (flags & CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL) == 0) { // release focus immediately to kick off focus change transition mNotificationShadeWindowController.setNotificationShadeFocusable(false); mNotificationShadeWindowViewController.cancelExpandHelper(); - mNotificationPanelViewController.collapse(true, delayed, speedUpFactor); + getShadeViewController().collapse(true, delayed, speedUpFactor); } } @@ -138,7 +139,7 @@ public final class ShadeControllerImpl implements ShadeController { if (!mCommandQueue.panelsEnabled()) { return; } - mNotificationPanelViewController.expandToNotifications(); + getShadeViewController().expandToNotifications(); } @Override @@ -149,12 +150,12 @@ public final class ShadeControllerImpl implements ShadeController { // Settings are not available in setup if (!mDeviceProvisionedController.isCurrentUserSetup()) return; - mNotificationPanelViewController.expandToQs(); + getShadeViewController().expandToQs(); } @Override public boolean closeShadeIfOpen() { - if (!mNotificationPanelViewController.isFullyCollapsed()) { + if (!getShadeViewController().isFullyCollapsed()) { mCommandQueue.animateCollapsePanels( CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */); notifyVisibilityChanged(false); @@ -170,12 +171,12 @@ public final class ShadeControllerImpl implements ShadeController { @Override public boolean isShadeFullyOpen() { - return mNotificationPanelViewController.isShadeFullyExpanded(); + return getShadeViewController().isShadeFullyExpanded(); } @Override public boolean isExpandingOrCollapsing() { - return mNotificationPanelViewController.isExpandingOrCollapsing(); + return getShadeViewController().isExpandingOrCollapsing(); } @Override public void postAnimateCollapseShade() { @@ -194,13 +195,13 @@ public final class ShadeControllerImpl implements ShadeController { @Override public void postOnShadeExpanded(Runnable executable) { - mNotificationPanelViewController.addOnGlobalLayoutListener( + getShadeViewController().addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { if (getNotificationShadeWindowView().isVisibleToUser()) { - mNotificationPanelViewController.removeOnGlobalLayoutListener(this); - mNotificationPanelViewController.postToView(executable); + getShadeViewController().removeOnGlobalLayoutListener(this); + getShadeViewController().postToView(executable); } } }); @@ -224,7 +225,7 @@ public final class ShadeControllerImpl implements ShadeController { @Override public boolean collapseShade() { - if (!mNotificationPanelViewController.isFullyCollapsed()) { + if (!getShadeViewController().isFullyCollapsed()) { // close the shade if it was open animateCollapseShadeForcedDelayed(); notifyVisibilityChanged(false); @@ -252,10 +253,10 @@ public final class ShadeControllerImpl implements ShadeController { @Override public void cancelExpansionAndCollapseShade() { - if (mNotificationPanelViewController.isTracking()) { + if (getShadeViewController().isTracking()) { mNotificationShadeWindowViewController.cancelCurrentTouch(); } - if (mNotificationPanelViewController.isPanelExpanded() + if (getShadeViewController().isPanelExpanded() && mStatusBarStateController.getState() == StatusBarState.SHADE) { animateCollapseShade(); } @@ -311,7 +312,7 @@ public final class ShadeControllerImpl implements ShadeController { @Override public void instantCollapseShade() { - mNotificationPanelViewController.instantCollapse(); + getShadeViewController().instantCollapse(); runPostCollapseRunnables(); } @@ -342,7 +343,7 @@ public final class ShadeControllerImpl implements ShadeController { } // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868) - mNotificationPanelViewController.collapse(false, false, 1.0f); + getShadeViewController().collapse(false, false, 1.0f); mExpandedVisible = false; notifyVisibilityChanged(false); @@ -364,7 +365,7 @@ public final class ShadeControllerImpl implements ShadeController { notifyExpandedVisibleChanged(false); mCommandQueue.recomputeDisableFlags( mDisplayId, - mNotificationPanelViewController.shouldHideStatusBarIconsWhenExpanded()); + getShadeViewController().shouldHideStatusBarIconsWhenExpanded()); // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in // the bouncer appear animation. @@ -406,11 +407,14 @@ public final class ShadeControllerImpl implements ShadeController { return mNotificationShadeWindowViewController.getView(); } + private ShadeViewController getShadeViewController() { + return mShadeViewControllerLazy.get(); + } + @Override - public void setShadeViewController(ShadeViewController shadeViewController) { - mNotificationPanelViewController = shadeViewController; - mNotificationPanelViewController.setTrackingStartedListener(this::runPostCollapseRunnables); - mNotificationPanelViewController.setOpenCloseListener( + public void start() { + getShadeViewController().setTrackingStartedListener(this::runPostCollapseRunnables); + getShadeViewController().setOpenCloseListener( new OpenCloseListener() { @Override public void onClosingFinished() { diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt index 8789a8b3b7f4..411f91f0cb07 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt @@ -131,6 +131,7 @@ constructor( private val date: TextView = header.findViewById(R.id.date) private val iconContainer: StatusIconContainer = header.findViewById(R.id.statusIcons) private val mShadeCarrierGroup: ShadeCarrierGroup = header.findViewById(R.id.carrier_group) + private val systemIcons: View = header.findViewById(R.id.shade_header_system_icons) private var roundedCorners = 0 private var cutout: DisplayCutout? = null @@ -254,6 +255,14 @@ constructor( header.paddingRight, header.paddingBottom ) + systemIcons.setPaddingRelative( + resources.getDimensionPixelSize( + R.dimen.shade_header_system_icons_padding_start + ), + systemIcons.paddingTop, + resources.getDimensionPixelSize(R.dimen.shade_header_system_icons_padding_end), + systemIcons.paddingBottom + ) } override fun onDensityOrFontScaleChanged() { @@ -266,6 +275,7 @@ constructor( lastInsets?.let { updateConstraintsForInsets(header, it) } updateResources() updateCarrierGroupPadding() + clock.onDensityOrFontScaleChanged() } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt index 2c560c952732..e2e633a3e665 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt @@ -59,7 +59,7 @@ import javax.inject.Named import javax.inject.Provider /** Module for classes related to the notification shade. */ -@Module +@Module(includes = [StartShadeModule::class]) abstract class ShadeModule { @Binds diff --git a/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt new file mode 100644 index 000000000000..384fb9543a8f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt @@ -0,0 +1,15 @@ +package com.android.systemui.shade + +import com.android.systemui.CoreStartable +import dagger.Binds +import dagger.Module +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap + +@Module +internal abstract class StartShadeModule { + @Binds + @IntoMap + @ClassKey(ShadeController::class) + abstract fun bind(shadeController: ShadeController): CoreStartable +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt index 776956a20140..56390002490c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt @@ -30,9 +30,11 @@ import androidx.core.animation.Animator import androidx.core.animation.AnimatorListenerAdapter import androidx.core.animation.AnimatorSet import androidx.core.animation.ValueAnimator +import com.android.internal.annotations.VisibleForTesting import com.android.systemui.R import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider import com.android.systemui.statusbar.window.StatusBarWindowController import com.android.systemui.util.animation.AnimationUtil.Companion.frames @@ -46,7 +48,7 @@ class SystemEventChipAnimationController @Inject constructor( private val context: Context, private val statusBarWindowController: StatusBarWindowController, private val contentInsetsProvider: StatusBarContentInsetsProvider, - private val featureFlags: FeatureFlags + private val featureFlags: FeatureFlags, ) : SystemStatusAnimationCallback { private lateinit var animationWindowView: FrameLayout @@ -56,7 +58,8 @@ class SystemEventChipAnimationController @Inject constructor( // Left for LTR, Right for RTL private var animationDirection = LEFT - private var chipBounds = Rect() + + @VisibleForTesting var chipBounds = Rect() private val chipWidth get() = chipBounds.width() private val chipRight get() = chipBounds.right private val chipLeft get() = chipBounds.left @@ -69,7 +72,7 @@ class SystemEventChipAnimationController @Inject constructor( private var animRect = Rect() // TODO: move to dagger - private var initialized = false + @VisibleForTesting var initialized = false /** * Give the chip controller a chance to inflate and configure the chip view before we start @@ -98,23 +101,7 @@ class SystemEventChipAnimationController @Inject constructor( View.MeasureSpec.makeMeasureSpec( (animationWindowView.parent as View).height, AT_MOST)) - // decide which direction we're animating from, and then set some screen coordinates - val contentRect = contentInsetsProvider.getStatusBarContentAreaForCurrentRotation() - val chipTop = ((animationWindowView.parent as View).height - it.view.measuredHeight) / 2 - val chipBottom = chipTop + it.view.measuredHeight - val chipRight: Int - val chipLeft: Int - when (animationDirection) { - LEFT -> { - chipRight = contentRect.right - chipLeft = contentRect.right - it.chipWidth - } - else /* RIGHT */ -> { - chipLeft = contentRect.left - chipRight = contentRect.left + it.chipWidth - } - } - chipBounds = Rect(chipLeft, chipTop, chipRight, chipBottom) + updateChipBounds(it, contentInsetsProvider.getStatusBarContentAreaForCurrentRotation()) } } @@ -253,16 +240,67 @@ class SystemEventChipAnimationController @Inject constructor( return animSet } - private fun init() { + fun init() { initialized = true themedContext = ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings) animationWindowView = LayoutInflater.from(themedContext) .inflate(R.layout.system_event_animation_window, null) as FrameLayout - val lp = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT) - lp.gravity = Gravity.END or Gravity.CENTER_VERTICAL + // Matches status_bar.xml + val height = themedContext.resources.getDimensionPixelSize(R.dimen.status_bar_height) + val lp = FrameLayout.LayoutParams(MATCH_PARENT, height) + lp.gravity = Gravity.END or Gravity.TOP statusBarWindowController.addViewToWindow(animationWindowView, lp) animationWindowView.clipToPadding = false animationWindowView.clipChildren = false + + // Use contentInsetsProvider rather than configuration controller, since we only care + // about status bar dimens + contentInsetsProvider.addCallback(object : StatusBarContentInsetsChangedListener { + override fun onStatusBarContentInsetsChanged() { + val newContentArea = contentInsetsProvider + .getStatusBarContentAreaForCurrentRotation() + updateDimens(newContentArea) + + // If we are currently animating, we have to re-solve for the chip bounds. If we're + // not animating then [prepareChipAnimation] will take care of it for us + currentAnimatedView?.let { + updateChipBounds(it, newContentArea) + } + } + }) + } + + private fun updateDimens(contentArea: Rect) { + val lp = animationWindowView.layoutParams as FrameLayout.LayoutParams + lp.height = contentArea.height() + + animationWindowView.layoutParams = lp + } + + /** + * Use the current status bar content area and the current chip's measured size to update + * the animation rect and chipBounds. This method can be called at any time and will update + * the current animation values properly during e.g. a rotation. + */ + private fun updateChipBounds(chip: BackgroundAnimatableView, contentArea: Rect) { + // decide which direction we're animating from, and then set some screen coordinates + val chipTop = (contentArea.bottom - chip.view.measuredHeight) / 2 + val chipBottom = chipTop + chip.view.measuredHeight + val chipRight: Int + val chipLeft: Int + + when (animationDirection) { + LEFT -> { + chipRight = contentArea.right + chipLeft = contentArea.right - chip.chipWidth + } + else /* RIGHT */ -> { + chipLeft = contentArea.left + chipRight = contentArea.left + chip.chipWidth + } + } + chipBounds = Rect(chipLeft, chipTop, chipRight, chipBottom) + animRect.set(chipBounds) } private fun layoutParamsDefault(marginEnd: Int): FrameLayout.LayoutParams = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 8902a186c43a..278ae95b7f28 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -1613,7 +1613,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // (Right now, there's a circular dependency.) mNotificationShadeWindowController.setWindowRootView(windowRootView); mNotificationShadeWindowViewController.setupExpandedStatusBar(); - mShadeController.setShadeViewController(mShadeSurface); mShadeController.setNotificationShadeWindowViewController( mNotificationShadeWindowViewController); mBackActionInteractor.setup(mQsController, mShadeSurface); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index 15ca37a222e5..418f9203761a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -448,9 +448,14 @@ public class NotificationIconContainer extends ViewGroup { @VisibleForTesting boolean isOverflowing(boolean isLastChild, float translationX, float layoutEnd, float iconSize) { - // Layout end, as used here, does not include padding end. - final float overflowX = isLastChild ? layoutEnd : layoutEnd - iconSize; - return translationX >= overflowX; + if (isLastChild) { + return translationX + iconSize > layoutEnd; + } else { + // If the child is not the last child, we need to ensure that we have room for the next + // icon and the dot. The dot could be as large as an icon, so verify that we have room + // for 2 icons. + return translationX + iconSize * 2f > layoutEnd; + } } /** @@ -490,10 +495,7 @@ public class NotificationIconContainer extends ViewGroup { // First icon to overflow. if (firstOverflowIndex == -1 && isOverflowing) { firstOverflowIndex = i; - mVisualOverflowStart = layoutEnd - mIconSize; - if (forceOverflow || mIsStaticLayout) { - mVisualOverflowStart = Math.min(translationX, mVisualOverflowStart); - } + mVisualOverflowStart = translationX; } final float drawingScale = mOnLockScreen && view instanceof StatusBarIconView ? ((StatusBarIconView) view).getIconScaleIncreased() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index cb2a78d8be35..5c1dfbe42cf9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -51,6 +51,7 @@ import com.android.keyguard.KeyguardSecurityModel; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.KeyguardViewController; +import com.android.keyguard.TrustGrantFlags; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; @@ -306,6 +307,16 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Nullable private TaskbarDelegate mTaskbarDelegate; private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { + @Override + public void onTrustGrantedForCurrentUser( + boolean dismissKeyguard, + boolean newlyUnlocked, + @NonNull TrustGrantFlags flags, + @Nullable String message + ) { + updateAlternateBouncerShowing(mAlternateBouncerInteractor.maybeHide()); + } + @Override public void onEmergencyCallAction() { // Since we won't get a setOccluded call we have to reset the view manually such that @@ -431,7 +442,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mDockManager.addListener(mDockEventListener); mIsDocked = mDockManager.isDocked(); } - mKeyguardStateController.addCallback(mKeyguardStateControllerCallback); } /** Register a callback, to be invoked by the Predictive Back system. */ @@ -1570,14 +1580,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb || mode == KeyguardSecurityModel.SecurityMode.SimPuk; } - private KeyguardStateController.Callback mKeyguardStateControllerCallback = - new KeyguardStateController.Callback() { - @Override - public void onUnlockedChanged() { - updateAlternateBouncerShowing(mAlternateBouncerInteractor.maybeHide()); - } - }; - /** * Delegate used to send show and hide events to an alternate authentication method instead of * the regular pin/pattern/password bouncer. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java index 3074abec860d..d83664f9156a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java @@ -22,6 +22,8 @@ import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON; import android.annotation.Nullable; import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; @@ -73,13 +75,16 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { // Any ignored icon will never be added as a child private ArrayList<String> mIgnoredSlots = new ArrayList<>(); + private Configuration mConfiguration; + public StatusIconContainer(Context context) { this(context, null); } public StatusIconContainer(Context context, AttributeSet attrs) { super(context, attrs); - initDimens(); + mConfiguration = new Configuration(context.getResources().getConfiguration()); + reloadDimens(); setWillNotDraw(!DEBUG_OVERFLOW); } @@ -100,7 +105,7 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { return mShouldRestrictIcons; } - private void initDimens() { + private void reloadDimens() { // This is the same value that StatusBarIconView uses mIconDotFrameWidth = getResources().getDimensionPixelSize( com.android.internal.R.dimen.status_bar_icon_size_sp); @@ -233,6 +238,16 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { child.setTag(R.id.status_bar_view_state_tag, null); } + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + final int configDiff = newConfig.diff(mConfiguration); + mConfiguration.setTo(newConfig); + if ((configDiff & (ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_FONT_SCALE)) != 0) { + reloadDimens(); + } + } + /** * Add a name of an icon slot to be ignored. It will not show up nor be measured * @param slotName name of the icon as it exists in @@ -342,13 +357,17 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { int totalVisible = mLayoutStates.size(); int maxVisible = totalVisible <= MAX_ICONS ? MAX_ICONS : MAX_ICONS - 1; - mUnderflowStart = 0; + // Init mUnderflowStart value with the offset to let the dot be placed next to battery icon. + // This is to prevent if the underflow happens at rightest(totalVisible - 1) child then + // break the for loop with mUnderflowStart staying 0(initial value), causing the dot be + // placed at the leftest side. + mUnderflowStart = (int) Math.max(contentStart, width - getPaddingEnd() - mUnderflowWidth); int visible = 0; int firstUnderflowIndex = -1; for (int i = totalVisible - 1; i >= 0; i--) { StatusIconState state = mLayoutStates.get(i); // Allow room for underflow if we found we need it in onMeasure - if (mNeedsUnderflow && (state.getXTranslation() < (contentStart + mUnderflowWidth)) + if ((mNeedsUnderflow && (state.getXTranslation() < (contentStart + mUnderflowWidth))) || (mShouldRestrictIcons && (visible >= maxVisible))) { firstUnderflowIndex = i; break; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt index 4e52be91f0af..7f35dfbe2700 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt @@ -34,6 +34,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.log.table.logDiffsForTable @@ -48,6 +49,7 @@ import com.android.systemui.statusbar.pipeline.wifi.shared.WifiInputLogger import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel import java.util.concurrent.Executor import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.awaitClose @@ -60,7 +62,9 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.withContext /** Real implementation of [WifiRepository]. */ @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") @@ -76,8 +80,9 @@ constructor( logger: WifiInputLogger, @WifiTableLog wifiTableLogBuffer: TableLogBuffer, @Main mainExecutor: Executor, + @Background private val bgDispatcher: CoroutineDispatcher, @Application scope: CoroutineScope, - wifiManager: WifiManager, + private val wifiManager: WifiManager, ) : RealWifiRepository { private val wifiStateChangeEvents: Flow<Unit> = @@ -93,20 +98,25 @@ constructor( // have changed. override val isWifiEnabled: StateFlow<Boolean> = merge(wifiNetworkChangeEvents, wifiStateChangeEvents) - .mapLatest { wifiManager.isWifiEnabled } + .onStart { emit(Unit) } + .mapLatest { isWifiEnabled() } .distinctUntilChanged() .logDiffsForTable( wifiTableLogBuffer, columnPrefix = "", columnName = "isEnabled", - initialValue = wifiManager.isWifiEnabled, + initialValue = false, ) .stateIn( scope = scope, - started = SharingStarted.WhileSubscribed(), - initialValue = wifiManager.isWifiEnabled, + started = SharingStarted.Eagerly, + initialValue = false, ) + // [WifiManager.isWifiEnabled] is a blocking IPC call, so fetch it in the background. + private suspend fun isWifiEnabled(): Boolean = + withContext(bgDispatcher) { wifiManager.isWifiEnabled } + override val isWifiDefault: StateFlow<Boolean> = connectivityRepository.defaultConnections // TODO(b/274493701): Should wifi be considered default if it's carrier merged? @@ -289,6 +299,7 @@ constructor( private val logger: WifiInputLogger, @WifiTableLog private val wifiTableLogBuffer: TableLogBuffer, @Main private val mainExecutor: Executor, + @Background private val bgDispatcher: CoroutineDispatcher, @Application private val scope: CoroutineScope, ) { fun create(wifiManager: WifiManager): WifiRepositoryImpl { @@ -299,6 +310,7 @@ constructor( logger, wifiTableLogBuffer, mainExecutor, + bgDispatcher, scope, wifiManager, ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java index 6875b523a962..f9943729ac7d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java @@ -374,6 +374,13 @@ public class Clock extends TextView implements @Override public void onDensityOrFontScaleChanged() { + reloadDimens(); + } + + private void reloadDimens() { + // reset mCachedWidth so the new width would be updated properly when next onMeasure + mCachedWidth = -1; + FontSizeUtils.updateFontSize(this, R.dimen.status_bar_clock_size); setPaddingRelative( mContext.getResources().getDimensionPixelSize( diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java index 3f31ff94fee7..8cf71a0b78c0 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java @@ -45,7 +45,7 @@ import com.android.systemui.screenshot.ReferenceScreenshotModule; import com.android.systemui.settings.dagger.MultiUserUtilsModule; import com.android.systemui.shade.NotificationShadeWindowControllerImpl; import com.android.systemui.shade.ShadeController; -import com.android.systemui.shade.ShadeControllerImpl; +import com.android.systemui.shade.ShadeControllerEmptyImpl; import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyboardShortcutsModule; @@ -140,7 +140,7 @@ public abstract class TvSystemUIModule { abstract DockManager bindDockManager(DockManagerImpl dockManager); @Binds - abstract ShadeController provideShadeController(ShadeControllerImpl shadeController); + abstract ShadeController provideShadeController(ShadeControllerEmptyImpl shadeController); @SysUISingleton @Provides diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt index fff1b81db628..278a43ea1bf1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt @@ -18,7 +18,7 @@ package com.android.systemui.biometrics.ui.viewmodel import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.biometrics.domain.model.BiometricModality +import com.android.systemui.biometrics.shared.model.BiometricModality import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt index 87c9e583af4d..91140a9b0fc4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt @@ -27,11 +27,12 @@ import com.android.systemui.biometrics.data.repository.FakePromptRepository import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl import com.android.systemui.biometrics.domain.model.BiometricModalities -import com.android.systemui.biometrics.domain.model.BiometricModality import com.android.systemui.biometrics.extractAuthenticatorTypes import com.android.systemui.biometrics.faceSensorPropertiesInternal import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal +import com.android.systemui.biometrics.shared.model.BiometricModality import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.coroutines.collectValues import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.util.mockito.any import com.google.common.truth.Truth.assertThat @@ -131,20 +132,22 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa } @Test - fun plays_haptic_on_authenticated() = runGenericTest { - viewModel.showAuthenticated(testCase.authenticatedModality, 1000L) + fun play_haptic_on_confirm_when_confirmation_required_otherwise_on_authenticated() = + runGenericTest { + val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false) - verify(vibrator).vibrateAuthSuccess(any()) - verify(vibrator, never()).vibrateAuthError(any()) - } + viewModel.showAuthenticated(testCase.authenticatedModality, 1_000L) - @Test - fun plays_no_haptic_on_confirm() = runGenericTest { - viewModel.confirmAuthenticated() + verify(vibrator, if (expectConfirmation) never() else times(1)) + .vibrateAuthSuccess(any()) - verify(vibrator, never()).vibrateAuthSuccess(any()) - verify(vibrator, never()).vibrateAuthError(any()) - } + if (expectConfirmation) { + viewModel.confirmAuthenticated() + } + + verify(vibrator).vibrateAuthSuccess(any()) + verify(vibrator, never()).vibrateAuthError(any()) + } private suspend fun TestScope.showAuthenticated( authenticatedModality: BiometricModality, @@ -204,7 +207,12 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa @Test fun plays_haptic_on_errors() = runGenericTest { - viewModel.showTemporaryError("so sad", hapticFeedback = true) + viewModel.showTemporaryError( + "so sad", + messageAfterError = "", + authenticateAfterError = false, + hapticFeedback = true, + ) verify(vibrator).vibrateAuthError(any()) verify(vibrator, never()).vibrateAuthSuccess(any()) @@ -212,7 +220,12 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa @Test fun plays_haptic_on_errors_unless_skipped() = runGenericTest { - viewModel.showTemporaryError("still sad", hapticFeedback = false) + viewModel.showTemporaryError( + "still sad", + messageAfterError = "", + authenticateAfterError = false, + hapticFeedback = false, + ) verify(vibrator, never()).vibrateAuthError(any()) verify(vibrator, never()).vibrateAuthSuccess(any()) @@ -287,7 +300,13 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa assertThat(canTryAgain).isFalse() } - val errorJob = launch { viewModel.showTemporaryError("error") } + val errorJob = launch { + viewModel.showTemporaryError( + "error", + messageAfterError = "", + authenticateAfterError = false, + ) + } verifyNoError() errorJob.join() verifyNoError() @@ -306,12 +325,66 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa assertThat(messageIsShowing).isTrue() } - // @Test - fun `suppress errors`() = runGenericTest { - val errorMessage = "woot" - val message by collectLastValue(viewModel.message) + @Test + fun suppress_temporary_error() = runGenericTest { + val messages by collectValues(viewModel.message) + + for (error in listOf("never", "see", "me")) { + launch { + viewModel.showTemporaryError( + error, + messageAfterError = "or me", + authenticateAfterError = false, + suppressIf = { _ -> true }, + ) + } + } + + testScheduler.advanceUntilIdle() + assertThat(messages).containsExactly(PromptMessage.Empty) + } - val errorJob = launch { viewModel.showTemporaryError(errorMessage) } + @Test + fun suppress_temporary_error_when_already_showing_when_requested() = + suppress_temporary_error_when_already_showing(suppress = true) + + @Test + fun do_not_suppress_temporary_error_when_already_showing_when_not_requested() = + suppress_temporary_error_when_already_showing(suppress = false) + + private fun suppress_temporary_error_when_already_showing(suppress: Boolean) = runGenericTest { + val errors = listOf("woot", "oh yeah", "nope") + val afterSuffix = "(after)" + val expectedErrorMessage = if (suppress) errors.first() else errors.last() + val messages by collectValues(viewModel.message) + + for (error in errors) { + launch { + viewModel.showTemporaryError( + error, + messageAfterError = "$error $afterSuffix", + authenticateAfterError = false, + suppressIf = { currentMessage -> suppress && currentMessage.isError }, + ) + } + } + + testScheduler.runCurrent() + assertThat(messages) + .containsExactly( + PromptMessage.Empty, + PromptMessage.Error(expectedErrorMessage), + ) + .inOrder() + + testScheduler.advanceUntilIdle() + assertThat(messages) + .containsExactly( + PromptMessage.Empty, + PromptMessage.Error(expectedErrorMessage), + PromptMessage.Help("$expectedErrorMessage $afterSuffix"), + ) + .inOrder() } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt index c2219a4d82eb..481f36e8ea38 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt @@ -345,23 +345,6 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) } - @Test - fun switchesToGone_whenUnlocked() = - testScope.runTest { - utils.authenticationRepository.setUnlocked(false) - sceneInteractor.setCurrentScene( - SceneTestUtils.CONTAINER_1, - SceneModel(SceneKey.Bouncer) - ) - val currentScene by - collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - - utils.authenticationRepository.setUnlocked(true) - - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) - } - private fun assertTryAgainMessage( message: String?, time: Int, diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt index 0356036fa78f..0df0a17931f4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt @@ -52,7 +52,10 @@ class BouncerViewModelTest : SysuiTestCase() { authenticationInteractor = authenticationInteractor, sceneInteractor = utils.sceneInteractor(), ) - private val underTest = utils.bouncerViewModel(bouncerInteractor) + private val underTest = + utils.bouncerViewModel( + bouncerInteractor = bouncerInteractor, + ) @Test fun authMethod_nonNullForSecureMethods_nullForNotSecureMethods() = diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt index 7d6c4a1a1455..5c6d4c69b50b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt @@ -20,7 +20,6 @@ import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.shared.model.AuthenticationMethodModel -import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.model.SceneKey @@ -54,16 +53,8 @@ class PinBouncerViewModelTest : SysuiTestCase() { sceneInteractor = sceneInteractor, ) private val bouncerViewModel = - BouncerViewModel( - applicationContext = context, - applicationScope = testScope.backgroundScope, - interactorFactory = - object : BouncerInteractor.Factory { - override fun create(containerName: String): BouncerInteractor { - return bouncerInteractor - } - }, - containerName = SceneTestUtils.CONTAINER_1, + utils.bouncerViewModel( + bouncerInteractor = bouncerInteractor, ) private val underTest = PinBouncerViewModel( diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt index 8b36284b8620..ca6a5b6234b9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt @@ -47,7 +47,6 @@ class LockscreenSceneInteractorTest : SysuiTestCase() { private val underTest = utils.lockScreenSceneInteractor( authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, bouncerInteractor = utils.bouncerInteractor( authenticationInteractor = authenticationInteractor, @@ -129,22 +128,6 @@ class LockscreenSceneInteractorTest : SysuiTestCase() { } @Test - fun deviceLockedInNonLockScreenScene_switchesToLockScreenScene() = - testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) - runCurrent() - sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Gone)) - runCurrent() - utils.authenticationRepository.setUnlocked(true) - runCurrent() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) - - utils.authenticationRepository.setUnlocked(false) - - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen)) - } - - @Test fun switchFromLockScreenToGone_authMethodNotSwipe_doesNotUnlockDevice() = testScope.runTest { val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt index 161dd660671e..ba8e0f277b6b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt @@ -56,7 +56,6 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { override fun create(containerName: String): LockscreenSceneInteractor { return utils.lockScreenSceneInteractor( authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, bouncerInteractor = utils.bouncerInteractor( authenticationInteractor = authenticationInteractor, diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt index 4eedc99839c6..ed7a59ea7032 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt @@ -53,7 +53,6 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { override fun create(containerName: String): LockscreenSceneInteractor { return utils.lockScreenSceneInteractor( authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, bouncerInteractor = utils.bouncerInteractor( authenticationInteractor = authenticationInteractor, diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt index df3701edc6de..df5e7bcd16be 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt @@ -14,14 +14,11 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class, ExperimentalCoroutinesApi::class) - package com.android.systemui.scene.domain.startable import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.model.SceneContainerNames @@ -29,11 +26,14 @@ import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.map import kotlinx.coroutines.test.runTest +import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(JUnit4::class) class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() { @@ -41,29 +41,47 @@ class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() { private val utils = SceneTestUtils(this) private val testScope = utils.testScope private val sceneInteractor = utils.sceneInteractor() - private val featureFlags = FakeFeatureFlags() + private val featureFlags = utils.featureFlags + private val authenticationRepository = utils.authenticationRepository() + private val authenticationInteractor = + utils.authenticationInteractor( + repository = authenticationRepository, + ) private val underTest = SystemUiDefaultSceneContainerStartable( applicationScope = testScope.backgroundScope, sceneInteractor = sceneInteractor, + authenticationInteractor = authenticationInteractor, featureFlags = featureFlags, ) + @Before + fun setUp() { + prepareState() + } + @Test - fun start_featureEnabled_keepsVisibilityUpdated() = + fun hydrateVisibility_featureEnabled() = testScope.runTest { - featureFlags.set(Flags.SCENE_CONTAINER, true) + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) val isVisible by collectLastValue(sceneInteractor.isVisible(SceneContainerNames.SYSTEM_UI_DEFAULT)) + prepareState( + isFeatureEnabled = true, + isDeviceUnlocked = true, + initialSceneKey = SceneKey.Gone, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) assertThat(isVisible).isTrue() underTest.start() - sceneInteractor.setCurrentScene( - SceneContainerNames.SYSTEM_UI_DEFAULT, - SceneModel(SceneKey.Gone) - ) assertThat(isVisible).isFalse() sceneInteractor.setCurrentScene( @@ -74,14 +92,26 @@ class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() { } @Test - fun start_featureDisabled_doesNotUpdateVisibility() = + fun hydrateVisibility_featureDisabled() = testScope.runTest { - featureFlags.set(Flags.SCENE_CONTAINER, false) + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) val isVisible by collectLastValue(sceneInteractor.isVisible(SceneContainerNames.SYSTEM_UI_DEFAULT)) + prepareState( + isFeatureEnabled = false, + isDeviceUnlocked = true, + initialSceneKey = SceneKey.Lockscreen, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) assertThat(isVisible).isTrue() underTest.start() + assertThat(isVisible).isTrue() sceneInteractor.setCurrentScene( SceneContainerNames.SYSTEM_UI_DEFAULT, @@ -95,4 +125,172 @@ class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() { ) assertThat(isVisible).isTrue() } + + @Test + fun switchToLockscreenWhenDeviceLocks_featureEnabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = true, + isDeviceUnlocked = true, + initialSceneKey = SceneKey.Gone, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + underTest.start() + + authenticationRepository.setUnlocked(false) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + } + + @Test + fun switchToLockscreenWhenDeviceLocks_featureDisabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = false, + isDeviceUnlocked = false, + initialSceneKey = SceneKey.Gone, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + underTest.start() + + authenticationRepository.setUnlocked(false) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + } + + @Test + fun switchFromBouncerToGoneWhenDeviceUnlocked_featureEnabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = true, + isDeviceUnlocked = false, + initialSceneKey = SceneKey.Bouncer, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer) + underTest.start() + + authenticationRepository.setUnlocked(true) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + } + + @Test + fun switchFromBouncerToGoneWhenDeviceUnlocked_featureDisabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = false, + isDeviceUnlocked = false, + initialSceneKey = SceneKey.Bouncer, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer) + underTest.start() + + authenticationRepository.setUnlocked(true) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer) + } + + @Test + fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn_featureOn_bypassOn() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = true, + isBypassEnabled = true, + initialSceneKey = SceneKey.Lockscreen, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + underTest.start() + + authenticationRepository.setUnlocked(true) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + } + + @Test + fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn_featureOn_bypassOff() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = true, + isBypassEnabled = false, + initialSceneKey = SceneKey.Lockscreen, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + underTest.start() + + authenticationRepository.setUnlocked(true) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + } + + @Test + fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn_featureOff_bypassOn() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = false, + isBypassEnabled = true, + initialSceneKey = SceneKey.Lockscreen, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + underTest.start() + + authenticationRepository.setUnlocked(true) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + } + + private fun prepareState( + isFeatureEnabled: Boolean = true, + isDeviceUnlocked: Boolean = false, + isBypassEnabled: Boolean = false, + initialSceneKey: SceneKey? = null, + ) { + featureFlags.set(Flags.SCENE_CONTAINER, isFeatureEnabled) + authenticationRepository.setUnlocked(isDeviceUnlocked) + authenticationRepository.setBypassEnabled(isBypassEnabled) + initialSceneKey?.let { + sceneInteractor.setCurrentScene(SceneContainerNames.SYSTEM_UI_DEFAULT, SceneModel(it)) + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt index 16751c937f9e..5c35913f6e20 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt @@ -16,12 +16,14 @@ package com.android.systemui.settings.brightness +import android.content.Intent import android.graphics.Rect import android.os.Handler import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View import android.view.ViewGroup +import android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY import androidx.test.filters.SmallTest import androidx.test.rule.ActivityTestRule import com.android.systemui.R @@ -29,15 +31,20 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.activity.SingleActivityFactory import com.android.systemui.settings.FakeDisplayTracker import com.android.systemui.settings.UserTracker +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper +import com.android.systemui.util.concurrency.DelayableExecutor +import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any +import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat -import java.util.concurrent.Executor import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.eq import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @@ -48,9 +55,12 @@ class BrightnessDialogTest : SysuiTestCase() { @Mock private lateinit var userTracker: UserTracker @Mock private lateinit var brightnessSliderControllerFactory: BrightnessSliderController.Factory - @Mock private lateinit var mainExecutor: Executor @Mock private lateinit var backgroundHandler: Handler @Mock private lateinit var brightnessSliderController: BrightnessSliderController + @Mock private lateinit var accessibilityMgr: AccessibilityManagerWrapper + + private val clock = FakeSystemClock() + private val mainExecutor = FakeExecutor(clock) private var displayTracker = FakeDisplayTracker(mContext) @@ -64,7 +74,8 @@ class BrightnessDialogTest : SysuiTestCase() { displayTracker, brightnessSliderControllerFactory, mainExecutor, - backgroundHandler + backgroundHandler, + accessibilityMgr ) }, /* initialTouchMode= */ false, @@ -77,8 +88,6 @@ class BrightnessDialogTest : SysuiTestCase() { `when`(brightnessSliderControllerFactory.create(any(), any())) .thenReturn(brightnessSliderController) `when`(brightnessSliderController.rootView).thenReturn(View(context)) - - activityRule.launchActivity(null) } @After @@ -88,6 +97,7 @@ class BrightnessDialogTest : SysuiTestCase() { @Test fun testGestureExclusion() { + activityRule.launchActivity(Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG)) val frame = activityRule.activity.requireViewById<View>(R.id.brightness_mirror_container) val lp = frame.layoutParams as ViewGroup.MarginLayoutParams @@ -104,18 +114,83 @@ class BrightnessDialogTest : SysuiTestCase() { .isEqualTo(Rect(-horizontalMargin, 0, frame.width + horizontalMargin, frame.height)) } + @Test + fun testTimeout() { + `when`( + accessibilityMgr.getRecommendedTimeoutMillis( + eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS), + anyInt() + ) + ) + .thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS) + val intent = Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG) + intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true) + activityRule.launchActivity(intent) + + assertThat(activityRule.activity.isFinishing()).isFalse() + + clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong()) + assertThat(activityRule.activity.isFinishing()).isTrue() + } + + @Test + fun testRestartTimeout() { + `when`( + accessibilityMgr.getRecommendedTimeoutMillis( + eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS), + anyInt() + ) + ) + .thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS) + val intent = Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG) + intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true) + activityRule.launchActivity(intent) + + assertThat(activityRule.activity.isFinishing()).isFalse() + + clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong() / 2) + // Restart the timeout + activityRule.activity.onResume() + + clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong() / 2) + // The dialog should not have disappeared yet + assertThat(activityRule.activity.isFinishing()).isFalse() + + clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong() / 2) + assertThat(activityRule.activity.isFinishing()).isTrue() + } + + @Test + fun testNoTimeoutIfNotStartedByBrightnessKey() { + `when`( + accessibilityMgr.getRecommendedTimeoutMillis( + eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS), + anyInt() + ) + ) + .thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS) + activityRule.launchActivity(Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG)) + + assertThat(activityRule.activity.isFinishing()).isFalse() + + clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong()) + assertThat(activityRule.activity.isFinishing()).isFalse() + } + class TestDialog( userTracker: UserTracker, displayTracker: FakeDisplayTracker, brightnessSliderControllerFactory: BrightnessSliderController.Factory, - mainExecutor: Executor, - backgroundHandler: Handler + mainExecutor: DelayableExecutor, + backgroundHandler: Handler, + accessibilityMgr: AccessibilityManagerWrapper ) : BrightnessDialog( userTracker, displayTracker, brightnessSliderControllerFactory, mainExecutor, - backgroundHandler + backgroundHandler, + accessibilityMgr ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt index 729c4a9145c2..52e0c9c9936b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt @@ -78,11 +78,11 @@ class ShadeControllerImplTest : SysuiTestCase() { deviceProvisionedController, notificationShadeWindowController, windowManager, + Lazy { shadeViewController }, Lazy { assistManager }, Lazy { gutsManager }, ) shadeController.setNotificationShadeWindowViewController(nswvc) - shadeController.setShadeViewController(shadeViewController) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt index 309ab058fb64..6e9fba64263b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt @@ -22,7 +22,6 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.domain.interactor.LockscreenSceneInteractor import com.android.systemui.scene.SceneTestUtils -import com.android.systemui.scene.SceneTestUtils.Companion.CONTAINER_1 import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.google.common.truth.Truth.assertThat @@ -54,7 +53,6 @@ class ShadeSceneViewModelTest : SysuiTestCase() { override fun create(containerName: String): LockscreenSceneInteractor { return utils.lockScreenSceneInteractor( authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, bouncerInteractor = utils.bouncerInteractor( authenticationInteractor = authenticationInteractor, @@ -89,7 +87,8 @@ class ShadeSceneViewModelTest : SysuiTestCase() { @Test fun onContentClicked_deviceUnlocked_switchesToGone() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(true) runCurrent() @@ -102,7 +101,8 @@ class ShadeSceneViewModelTest : SysuiTestCase() { @Test fun onContentClicked_deviceLockedSecurely_switchesToBouncer() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(false) runCurrent() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt new file mode 100644 index 000000000000..55b6be9679f2 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.events + +import android.content.Context +import android.graphics.Rect +import android.util.Pair +import android.view.Gravity +import android.view.View +import android.widget.FrameLayout +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener +import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider +import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.capture +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +class SystemEventChipAnimationControllerTest : SysuiTestCase() { + private lateinit var controller: SystemEventChipAnimationController + + @Mock private lateinit var sbWindowController: StatusBarWindowController + @Mock private lateinit var insetsProvider: StatusBarContentInsetsProvider + + private var testView = TestView(mContext) + private var viewCreator: ViewCreator = { testView } + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + + // StatusBarWindowController is mocked. The addViewToWindow function needs to be mocked to + // ensure that the chip view is added to a parent view + whenever(sbWindowController.addViewToWindow(any(), any())).then { + val statusbarFake = FrameLayout(mContext) + statusbarFake.layout( + portraitArea.left, + portraitArea.top, + portraitArea.right, + portraitArea.bottom, + ) + statusbarFake.addView( + it.arguments[0] as View, + it.arguments[1] as FrameLayout.LayoutParams + ) + } + + whenever(insetsProvider.getStatusBarContentInsetsForCurrentRotation()) + .thenReturn(Pair(insets, insets)) + whenever(insetsProvider.getStatusBarContentAreaForCurrentRotation()) + .thenReturn(portraitArea) + + controller = + SystemEventChipAnimationController( + context = mContext, + statusBarWindowController = sbWindowController, + contentInsetsProvider = insetsProvider, + featureFlags = FakeFeatureFlags(), + ) + } + + @Test + fun prepareChipAnimation_lazyInitializes() { + // Until Dagger can do our initialization, make sure that the first chip animation calls + // init() + assertFalse(controller.initialized) + controller.prepareChipAnimation(viewCreator) + assertTrue(controller.initialized) + } + + @Test + fun prepareChipAnimation_positionsChip() { + controller.prepareChipAnimation(viewCreator) + val chipRect = controller.chipBounds + + // SB area = 10, 0, 990, 100 + // chip size = 0, 0, 100, 50 + assertThat(chipRect).isEqualTo(Rect(890, 25, 990, 75)) + } + + @Test + fun prepareChipAnimation_rotation_repositionsChip() { + controller.prepareChipAnimation(viewCreator) + + // Chip has been prepared, and is located at (890, 25, 990, 75) + // Rotation should put it into its landscape location: + // SB area = 10, 0, 1990, 80 + // chip size = 0, 0, 100, 50 + + whenever(insetsProvider.getStatusBarContentAreaForCurrentRotation()) + .thenReturn(landscapeArea) + getInsetsListener().onStatusBarContentInsetsChanged() + + val chipRect = controller.chipBounds + assertThat(chipRect).isEqualTo(Rect(1890, 15, 1990, 65)) + } + + /** regression test for (b/289378932) */ + @Test + fun fullScreenStatusBar_positionsChipAtTop_withTopGravity() { + // In the case of a fullscreen status bar window, the content insets area is still correct + // (because it uses the dimens), but the window can be full screen. This seems to happen + // when launching an app from the ongoing call chip. + + // GIVEN layout the status bar window fullscreen portrait + whenever(sbWindowController.addViewToWindow(any(), any())).then { + val statusbarFake = FrameLayout(mContext) + statusbarFake.layout( + fullScreenSb.left, + fullScreenSb.top, + fullScreenSb.right, + fullScreenSb.bottom, + ) + + val lp = it.arguments[1] as FrameLayout.LayoutParams + assertThat(lp.gravity and Gravity.VERTICAL_GRAVITY_MASK).isEqualTo(Gravity.TOP) + + statusbarFake.addView( + it.arguments[0] as View, + lp, + ) + } + + // GIVEN insets provider gives the correct content area + whenever(insetsProvider.getStatusBarContentAreaForCurrentRotation()) + .thenReturn(portraitArea) + + // WHEN the controller lays out the chip in a fullscreen window + controller.prepareChipAnimation(viewCreator) + + // THEN it still aligns the chip to the content area provided by the insets provider + val chipRect = controller.chipBounds + assertThat(chipRect).isEqualTo(Rect(890, 25, 990, 75)) + } + + class TestView(context: Context) : View(context), BackgroundAnimatableView { + override val view: View + get() = this + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + setMeasuredDimension(100, 50) + } + + override fun setBoundsForAnimation(l: Int, t: Int, r: Int, b: Int) { + setLeftTopRightBottom(l, t, r, b) + } + } + + private fun getInsetsListener(): StatusBarContentInsetsChangedListener { + val callbackCaptor = argumentCaptor<StatusBarContentInsetsChangedListener>() + verify(insetsProvider).addCallback(capture(callbackCaptor)) + return callbackCaptor.value!! + } + + companion object { + private val portraitArea = Rect(10, 0, 990, 100) + private val landscapeArea = Rect(10, 0, 1990, 80) + private val fullScreenSb = Rect(10, 0, 990, 2000) + + // 10px insets on both sides + private const val insets = 10 + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index 88d8dfc50b47..3d35233ad646 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -447,10 +447,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mDeviceProvisionedController, mNotificationShadeWindowController, mContext.getSystemService(WindowManager.class), + () -> mNotificationPanelViewController, () -> mAssistManager, () -> mNotificationGutsManager )); - mShadeController.setShadeViewController(mNotificationPanelViewController); mShadeController.setNotificationShadeWindowViewController( mNotificationShadeWindowViewController); mShadeController.setNotificationPresenter(mNotificationPresenter); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt index b80b825d87dc..c282c1ef0cf6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt @@ -21,6 +21,8 @@ import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT +import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN import junit.framework.Assert.assertEquals import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue @@ -49,7 +51,7 @@ class NotificationIconContainerTest : SysuiTestCase() { fun calculateWidthFor_oneIcon_widthForOneIcon() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) - iconContainer.setIconSize(10); + iconContainer.setIconSize(10) assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 1f), /* actual= */ 30f) @@ -59,7 +61,7 @@ class NotificationIconContainerTest : SysuiTestCase() { fun calculateWidthFor_fourIcons_widthForFourIcons() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) - iconContainer.setIconSize(10); + iconContainer.setIconSize(10) assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 4f), /* actual= */ 60f) @@ -69,7 +71,7 @@ class NotificationIconContainerTest : SysuiTestCase() { fun calculateWidthFor_fiveIcons_widthForFourIcons() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) - iconContainer.setIconSize(10); + iconContainer.setIconSize(10) assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 5f), /* actual= */ 60f) } @@ -78,7 +80,7 @@ class NotificationIconContainerTest : SysuiTestCase() { fun calculateIconXTranslations_shortShelfOneIcon_atCorrectXWithoutOverflowDot() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) - iconContainer.setIconSize(10); + iconContainer.setIconSize(10) val icon = mockStatusBarIcon() iconContainer.addView(icon) @@ -99,7 +101,7 @@ class NotificationIconContainerTest : SysuiTestCase() { fun calculateIconXTranslations_shortShelfFourIcons_atCorrectXWithoutOverflowDot() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) - iconContainer.setIconSize(10); + iconContainer.setIconSize(10) val iconOne = mockStatusBarIcon() val iconTwo = mockStatusBarIcon() @@ -128,7 +130,7 @@ class NotificationIconContainerTest : SysuiTestCase() { fun calculateIconXTranslations_shortShelfFiveIcons_atCorrectXWithOverflowDot() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) - iconContainer.setIconSize(10); + iconContainer.setIconSize(10) val iconOne = mockStatusBarIcon() val iconTwo = mockStatusBarIcon() @@ -154,6 +156,55 @@ class NotificationIconContainerTest : SysuiTestCase() { } @Test + fun calculateIconXTranslations_givenWidthEnoughForThreeIcons_atCorrectXWithoutOverflowDot() { + iconContainer.setActualPaddingStart(0f) + iconContainer.setActualPaddingEnd(0f) + iconContainer.setActualLayoutWidth(30) + iconContainer.setIconSize(10) + + val iconOne = mockStatusBarIcon() + val iconTwo = mockStatusBarIcon() + val iconThree = mockStatusBarIcon() + + iconContainer.addView(iconOne) + iconContainer.addView(iconTwo) + iconContainer.addView(iconThree) + assertEquals(3, iconContainer.childCount) + + iconContainer.calculateIconXTranslations() + assertEquals(0f, iconContainer.getIconState(iconOne).xTranslation) + assertEquals(10f, iconContainer.getIconState(iconTwo).xTranslation) + assertEquals(20f, iconContainer.getIconState(iconThree).xTranslation) + assertFalse(iconContainer.areIconsOverflowing()) + } + + @Test + fun calculateIconXTranslations_givenWidthNotEnoughForFourIcons_atCorrectXWithOverflowDot() { + iconContainer.setActualPaddingStart(0f) + iconContainer.setActualPaddingEnd(0f) + iconContainer.setActualLayoutWidth(35) + iconContainer.setIconSize(10) + + val iconOne = mockStatusBarIcon() + val iconTwo = mockStatusBarIcon() + val iconThree = mockStatusBarIcon() + val iconFour = mockStatusBarIcon() + + iconContainer.addView(iconOne) + iconContainer.addView(iconTwo) + iconContainer.addView(iconThree) + iconContainer.addView(iconFour) + assertEquals(4, iconContainer.childCount) + + iconContainer.calculateIconXTranslations() + assertEquals(0f, iconContainer.getIconState(iconOne).xTranslation) + assertEquals(10f, iconContainer.getIconState(iconTwo).xTranslation) + assertEquals(STATE_DOT, iconContainer.getIconState(iconThree).visibleState) + assertEquals(STATE_HIDDEN, iconContainer.getIconState(iconFour).visibleState) + assertTrue(iconContainer.areIconsOverflowing()) + } + + @Test fun shouldForceOverflow_appearingAboveSpeedBump_true() { val forceOverflow = iconContainer.shouldForceOverflow( /* i= */ 1, @@ -161,7 +212,7 @@ class NotificationIconContainerTest : SysuiTestCase() { /* iconAppearAmount= */ 1f, /* maxVisibleIcons= */ 5 ) - assertTrue(forceOverflow); + assertTrue(forceOverflow) } @Test @@ -172,7 +223,7 @@ class NotificationIconContainerTest : SysuiTestCase() { /* iconAppearAmount= */ 0f, /* maxVisibleIcons= */ 5 ) - assertTrue(forceOverflow); + assertTrue(forceOverflow) } @Test @@ -183,7 +234,7 @@ class NotificationIconContainerTest : SysuiTestCase() { /* iconAppearAmount= */ 0f, /* maxVisibleIcons= */ 5 ) - assertFalse(forceOverflow); + assertFalse(forceOverflow) } @Test @@ -210,6 +261,17 @@ class NotificationIconContainerTest : SysuiTestCase() { } @Test + fun isOverflowing_lastChildXGreaterThanDotX_true() { + val isOverflowing = iconContainer.isOverflowing( + /* isLastChild= */ true, + /* translationX= */ 9f, + /* layoutEnd= */ 10f, + /* iconSize= */ 2f, + ) + assertTrue(isOverflowing) + } + + @Test fun isOverflowing_lastChildXGreaterThanLayoutEnd_true() { val isOverflowing = iconContainer.isOverflowing( /* isLastChild= */ true, @@ -253,7 +315,7 @@ class NotificationIconContainerTest : SysuiTestCase() { assertTrue(isOverflowing) } - private fun mockStatusBarIcon() : StatusBarIconView { + private fun mockStatusBarIcon(): StatusBarIconView { val iconView = mock(StatusBarIconView::class.java) whenever(iconView.width).thenReturn(10) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index c7143debf8a8..ed9cf3f2f158 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -35,6 +35,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.service.trust.TrustAgentService; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.MotionEvent; @@ -57,6 +58,8 @@ import com.android.keyguard.KeyguardMessageArea; import com.android.keyguard.KeyguardMessageAreaController; import com.android.keyguard.KeyguardSecurityModel; import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.keyguard.TrustGrantFlags; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor; @@ -84,7 +87,6 @@ import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.unfold.SysUIUnfoldComponent; import com.google.common.truth.Truth; @@ -154,7 +156,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Captor private ArgumentCaptor<OnBackInvokedCallback> mBackCallbackCaptor; @Captor - private ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateControllerCallback; + private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallback; @Before @@ -936,18 +938,24 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test - public void onDeviceUnlocked_hideAlternateBouncerAndClearMessageArea() { + public void onTrustChanged_hideAlternateBouncerAndClearMessageArea() { + // GIVEN keyguard update monitor callback is registered + verify(mKeyguardUpdateMonitor).registerCallback(mKeyguardUpdateMonitorCallback.capture()); + reset(mKeyguardUpdateMonitor); reset(mKeyguardMessageAreaController); - // GIVEN keyguard state controller callback is registered - verify(mKeyguardStateController).addCallback(mKeyguardStateControllerCallback.capture()); - // GIVEN alternate bouncer state = not visible when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false); - // WHEN the device is unlocked - mKeyguardStateControllerCallback.getValue().onUnlockedChanged(); + // WHEN the device is trusted by active unlock + mKeyguardUpdateMonitorCallback.getValue().onTrustGrantedForCurrentUser( + true, + true, + new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD + | TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE), + null + ); // THEN the false visibility state is propagated to the keyguardUpdateMonitor verify(mKeyguardUpdateMonitor).setAlternateBouncerShowing(eq(false)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt index 862eb001becc..c8b6f13d6902 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt @@ -159,6 +159,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { mock(), mock(), FakeExecutor(FakeSystemClock()), + dispatcher, testScope.backgroundScope, mock(), ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt index 30b95ef3b205..5bc98e0d19af 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt @@ -83,6 +83,7 @@ class WifiRepositorySwitcherTest : SysuiTestCase() { logger, tableLogger, mainExecutor, + testDispatcher, testScope.backgroundScope, wifiManager, ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt index 5ed3a5c7aa41..7007345c175c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt @@ -76,7 +76,8 @@ class WifiRepositoryImplTest : SysuiTestCase() { private lateinit var executor: Executor private lateinit var connectivityRepository: ConnectivityRepository - private val testScope = TestScope(UnconfinedTestDispatcher()) + private val dispatcher = UnconfinedTestDispatcher() + private val testScope = TestScope(dispatcher) @Before fun setUp() { @@ -1301,6 +1302,7 @@ class WifiRepositoryImplTest : SysuiTestCase() { logger, tableLogger, executor, + dispatcher, testScope.backgroundScope, wifiManager, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt index fec1187d8d11..d797962bae8b 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt @@ -24,6 +24,8 @@ import com.android.systemui.authentication.domain.interactor.AuthenticationInter import com.android.systemui.bouncer.data.repository.BouncerRepository import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel +import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.keyguard.domain.interactor.LockscreenSceneInteractor import com.android.systemui.scene.data.repository.SceneContainerRepository import com.android.systemui.scene.domain.interactor.SceneInteractor @@ -51,6 +53,7 @@ class SceneTestUtils( ) { val testDispatcher = StandardTestDispatcher() val testScope = TestScope(testDispatcher) + val featureFlags = FakeFeatureFlags().apply { set(Flags.SCENE_CONTAINER, true) } private val userRepository: UserRepository by lazy { FakeUserRepository().apply { val users = listOf(UserInfo(/* id= */ 0, "name", /* flags= */ 0)) @@ -129,6 +132,7 @@ class SceneTestUtils( repository = BouncerRepository(), authenticationInteractor = authenticationInteractor, sceneInteractor = sceneInteractor, + featureFlags = featureFlags, containerName = CONTAINER_1, ) } @@ -145,13 +149,13 @@ class SceneTestUtils( return bouncerInteractor } }, + featureFlags = featureFlags, containerName = CONTAINER_1, ) } fun lockScreenSceneInteractor( authenticationInteractor: AuthenticationInteractor, - sceneInteractor: SceneInteractor, bouncerInteractor: BouncerInteractor, ): LockscreenSceneInteractor { return LockscreenSceneInteractor( @@ -163,7 +167,6 @@ class SceneTestUtils( return bouncerInteractor } }, - sceneInteractor = sceneInteractor, containerName = CONTAINER_1, ) } diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java index 7fae31c0bb4b..bca2d60761d1 100644 --- a/services/core/java/com/android/server/SystemConfig.java +++ b/services/core/java/com/android/server/SystemConfig.java @@ -1827,6 +1827,9 @@ public class SystemConfig { soname, soname, new String[0], true); mSharedLibraries.put(entry.name, entry); } + } catch (FileNotFoundException e) { + // Expected for /vendor/etc/public.libraries.txt on some devices + Slog.d(TAG, listFile + " does not exist"); } catch (IOException e) { Slog.w(TAG, "Failed to read public libraries file " + listFile, e); } diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 48de441806c1..6baae4b39374 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -453,7 +453,7 @@ public class Watchdog implements Dumpable { mInterestingJavaPids.add(Process.myPid()); // See the notes on DEFAULT_TIMEOUT. - assert DB || + assert DB || Build.IS_USERDEBUG || DEFAULT_TIMEOUT > ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS; mTraceErrorLogger = new TraceErrorLogger(); @@ -527,7 +527,8 @@ public class Watchdog implements Dumpable { */ void updateWatchdogTimeout(long timeoutMillis) { // See the notes on DEFAULT_TIMEOUT. - if (!DB && timeoutMillis <= ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS) { + if (!DB && !Build.IS_USERDEBUG + && timeoutMillis <= ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS) { timeoutMillis = ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS + 1; } mWatchdogTimeoutMillis = timeoutMillis; diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java index 3ab40d26c973..e26ee9c9d747 100644 --- a/services/core/java/com/android/server/am/ContentProviderHelper.java +++ b/services/core/java/com/android/server/am/ContentProviderHelper.java @@ -96,7 +96,9 @@ import com.android.server.sdksandbox.SdkSandboxManagerLocal; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; /** @@ -116,6 +118,8 @@ public class ContentProviderHelper { private final ProviderMap mProviderMap; private boolean mSystemProvidersInstalled; + private final Map<String, Boolean> mCloneProfileAuthorityRedirectionCache = new HashMap<>(); + ContentProviderHelper(ActivityManagerService service, boolean createProviderMap) { mService = service; mProviderMap = createProviderMap ? new ProviderMap(mService) : null; @@ -201,9 +205,24 @@ public class ContentProviderHelper { final UserProperties userProps = umInternal.getUserProperties(userId); final boolean isMediaSharedWithParent = userProps != null && userProps.isMediaSharedWithParent(); - if (!isAuthorityRedirectedForCloneProfile(name) || !isMediaSharedWithParent) { + if (!isAuthorityRedirectedForCloneProfileCached(name) || !isMediaSharedWithParent) { // First check if this content provider has been published... cpr = mProviderMap.getProviderByName(name, userId); + // In case we are on media authority and callingUid is cloned app asking to access + // the contentProvider of user 0 by specifying content as + // content://<parent-id>@media/external/file, we skip checkUser. + if (isAuthorityRedirectedForCloneProfileCached(name)) { + final int callingUserId = UserHandle.getUserId(callingUid); + final UserProperties userPropsCallingUser = + umInternal.getUserProperties(callingUserId); + final boolean isMediaSharedWithParentForCallingUser = + userPropsCallingUser != null + && userPropsCallingUser.isMediaSharedWithParent(); + if (isMediaSharedWithParentForCallingUser + && umInternal.getProfileParentId(callingUserId) == userId) { + checkCrossUser = false; + } + } } // If that didn't work, check if it exists for user 0 and then // verify that it's a singleton provider before using it. @@ -218,7 +237,7 @@ public class ContentProviderHelper { r == null ? callingUid : r.uid, cpi.applicationInfo.uid)) { userId = UserHandle.USER_SYSTEM; checkCrossUser = false; - } else if (isAuthorityRedirectedForCloneProfile(name)) { + } else if (isAuthorityRedirectedForCloneProfileCached(name)) { if (isMediaSharedWithParent) { userId = umInternal.getProfileParentId(userId); checkCrossUser = false; @@ -1073,7 +1092,7 @@ public class ContentProviderHelper { return false; } - if (isAuthorityRedirectedForCloneProfile(holder.info.authority) + if (isAuthorityRedirectedForCloneProfileCached(holder.info.authority) && resolveParentUserIdForCloneProfile(userId) != userId) { // Since clone profile shares certain providers with its parent and the access is // re-directed as well, the holder may not actually be installed on the clone profile. @@ -1108,7 +1127,7 @@ public class ContentProviderHelper { userId = UserHandle.getCallingUserId(); } - if (isAuthorityRedirectedForCloneProfile(authority)) { + if (isAuthorityRedirectedForCloneProfileCached(authority)) { UserManagerInternal umInternal = LocalServices.getService(UserManagerInternal.class); UserInfo userInfo = umInternal.getUserInfo(userId); @@ -1954,4 +1973,14 @@ public class ContentProviderHelper { String[] args) { return mProviderMap.dumpProviderProto(fd, pw, name, args); } + + private Boolean isAuthorityRedirectedForCloneProfileCached(String auth) { + if (mCloneProfileAuthorityRedirectionCache.containsKey(auth)) { + return mCloneProfileAuthorityRedirectionCache.get(auth); + } else { + boolean isAuthRedirected = isAuthorityRedirectedForCloneProfile(auth); + mCloneProfileAuthorityRedirectionCache.put(auth, isAuthRedirected); + return isAuthRedirected; + } + } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 81bcd2a28b4f..1bc681a1dad4 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -7005,7 +7005,7 @@ public class NotificationManagerService extends SystemService { */ private boolean canBeNonDismissible(ApplicationInfo ai, Notification notification) { return notification.isMediaNotification() || isEnterpriseExempted(ai) - || isCallNotification(ai.packageName, ai.uid, notification) + || notification.isStyle(Notification.CallStyle.class) || isDefaultSearchSelectorPackage(ai.packageName); } diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java index 7198de2f743e..3a1fd7cfb451 100644 --- a/services/core/java/com/android/server/pm/VerifyingSession.java +++ b/services/core/java/com/android/server/pm/VerifyingSession.java @@ -589,27 +589,14 @@ final class VerifyingSession { final PackageVerificationResponse response = new PackageVerificationResponse( verificationCodeAtTimeout, requiredUid); - if (streaming) { - // For streaming installations, count verification timeout from the broadcast. - startVerificationTimeoutCountdown(verificationId, streaming, response, - verificationTimeout); - } + startVerificationTimeoutCountdown(verificationId, streaming, response, + verificationTimeout); // Send the intent to the required verification agent, but only start the // verification timeout after the target BroadcastReceivers have run. mPm.mContext.sendOrderedBroadcastAsUser(requiredIntent, verifierUser, receiverPermission, AppOpsManager.OP_NONE, options.toBundle(), - new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (!streaming) { - // For NON-streaming installations, count verification timeout from - // the broadcast was processed by all receivers. - startVerificationTimeoutCountdown(verificationId, streaming, - response, verificationTimeout); - } - } - }, null, 0, null, null); + null, null, 0, null, null); } Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 6f5125cf293c..402bb5976632 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -3304,8 +3304,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { minLinearBrightness, maxLinearBrightness); mDisplayManager.setBrightness(screenDisplayId, adjustedLinearBrightness); - startActivityAsUser(new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG), - UserHandle.CURRENT_OR_SELF); + Intent intent = new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG); + intent.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION); + intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true); + startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF); } return true; case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN: diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 7feefe7de409..455e7ae8ee45 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1997,11 +1997,7 @@ class ActivityStarter { } } - boolean shouldBlockActivityStart = true; - // Used for logging/toasts. Would we block the start if target sdk was U and feature was - // enabled? - boolean wouldBlockActivityStartIgnoringFlags = true; - + Pair<Boolean, Boolean> pair = null; if (mSourceRecord != null) { boolean passesAsmChecks = true; Task sourceTask = mSourceRecord.getTask(); @@ -2017,14 +2013,25 @@ class ActivityStarter { if (passesAsmChecks) { Task taskToCheck = taskToFront ? sourceTask : targetTask; - // first == false means Should Block - // second == false means Would Block disregarding flags - Pair<Boolean, Boolean> pair = ActivityTaskSupervisor + pair = ActivityTaskSupervisor .doesTopActivityMatchingUidExistForAsm(taskToCheck, mSourceRecord.getUid(), mSourceRecord); - shouldBlockActivityStart = !pair.first; - wouldBlockActivityStartIgnoringFlags = !pair.second; } + } else if (!taskToFront) { + // We don't have a sourceRecord, and we're launching into an existing task. + // Allow if callingUid is top of stack. + pair = ActivityTaskSupervisor + .doesTopActivityMatchingUidExistForAsm(targetTask, mCallingUid, + /*sourceRecord*/null); + } + + boolean shouldBlockActivityStart = true; + if (pair != null) { + // We block if feature flag is enabled + shouldBlockActivityStart = !pair.first; + // Used for logging/toasts. Would we block if target sdk was U and feature was + // enabled? If so, we can't return here but we also might not block at the end + boolean wouldBlockActivityStartIgnoringFlags = !pair.second; if (!wouldBlockActivityStartIgnoringFlags) { return true; diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java index fda22cab6276..7a201a77c966 100644 --- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java +++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java @@ -85,6 +85,13 @@ final class LetterboxConfiguration { // TODO(b/288142656): Enable user aspect ratio settings by default. private static final boolean DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_SETTINGS = false; + // Whether the letterbox wallpaper style is enabled by default + private static final String KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER = + "enable_letterbox_background_wallpaper"; + + // TODO(b/290048978): Enable wallpaper as default letterbox background. + private static final boolean DEFAULT_VALUE_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER = false; + /** * Override of aspect ratio for fixed orientation letterboxing that is set via ADB with * set-fixed-orientation-letterbox-aspect-ratio or via {@link @@ -101,9 +108,16 @@ final class LetterboxConfiguration { /** Enum for Letterbox background type. */ @Retention(RetentionPolicy.SOURCE) - @IntDef({LETTERBOX_BACKGROUND_SOLID_COLOR, LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND, - LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING, LETTERBOX_BACKGROUND_WALLPAPER}) + @IntDef({LETTERBOX_BACKGROUND_OVERRIDE_UNSET, + LETTERBOX_BACKGROUND_SOLID_COLOR, + LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND, + LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING, + LETTERBOX_BACKGROUND_WALLPAPER}) @interface LetterboxBackgroundType {}; + + /** No letterbox background style set. Using the one defined by DeviceConfig. */ + static final int LETTERBOX_BACKGROUND_OVERRIDE_UNSET = -1; + /** Solid background using color specified in R.color.config_letterboxBackgroundColor. */ static final int LETTERBOX_BACKGROUND_SOLID_COLOR = 0; @@ -183,14 +197,14 @@ final class LetterboxConfiguration { @Nullable private Integer mLetterboxBackgroundColorResourceIdOverride; @LetterboxBackgroundType - private int mLetterboxBackgroundType; + private final int mLetterboxBackgroundType; - // Blur radius for LETTERBOX_BACKGROUND_WALLPAPER option in mLetterboxBackgroundType. + // Blur radius for LETTERBOX_BACKGROUND_WALLPAPER option from getLetterboxBackgroundType(). // Values <= 0 are ignored and 0 is used instead. - private int mLetterboxBackgroundWallpaperBlurRadius; + private int mLetterboxBackgroundWallpaperBlurRadiusPx; // Alpha of a black scrim shown over wallpaper letterbox background when - // LETTERBOX_BACKGROUND_WALLPAPER option is selected for mLetterboxBackgroundType. + // LETTERBOX_BACKGROUND_WALLPAPER option is returned from getLetterboxBackgroundType(). // Values < 0 or >= 1 are ignored and 0.0 (transparent) is used instead. private float mLetterboxBackgroundWallpaperDarkScrimAlpha; @@ -252,6 +266,11 @@ final class LetterboxConfiguration { // Allows to enable user aspect ratio settings ignoring flags. private boolean mUserAppAspectRatioSettingsOverrideEnabled; + // The override for letterbox background type in case it's different from + // LETTERBOX_BACKGROUND_OVERRIDE_UNSET + @LetterboxBackgroundType + private int mLetterboxBackgroundTypeOverride = LETTERBOX_BACKGROUND_OVERRIDE_UNSET; + // Whether we should use split screen aspect ratio for the activity when camera compat treatment // is enabled and activity is connected to the camera in fullscreen. private final boolean mIsCameraCompatSplitScreenAspectRatioEnabled; @@ -294,10 +313,10 @@ final class LetterboxConfiguration { mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat( R.dimen.config_fixedOrientationLetterboxAspectRatio); + mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext); mLetterboxActivityCornersRadius = mContext.getResources().getInteger( R.integer.config_letterboxActivityCornersRadius); - mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext); - mLetterboxBackgroundWallpaperBlurRadius = mContext.getResources().getDimensionPixelSize( + mLetterboxBackgroundWallpaperBlurRadiusPx = mContext.getResources().getDimensionPixelSize( R.dimen.config_letterboxBackgroundWallpaperBlurRadius); mLetterboxBackgroundWallpaperDarkScrimAlpha = mContext.getResources().getFloat( R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha); @@ -359,6 +378,8 @@ final class LetterboxConfiguration { DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_SETTINGS, mContext.getResources().getBoolean( R.bool.config_appCompatUserAppAspectRatioSettingsIsEnabled)) + .addDeviceConfigEntry(KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER, + DEFAULT_VALUE_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER, /* enabled */ true) .build(); } @@ -497,25 +518,39 @@ final class LetterboxConfiguration { } /** - * Gets {@link LetterboxBackgroundType} specified in {@link - * com.android.internal.R.integer.config_letterboxBackgroundType} or over via ADB command. + * Gets {@link LetterboxBackgroundType} specified via ADB command or the default one. */ @LetterboxBackgroundType int getLetterboxBackgroundType() { - return mLetterboxBackgroundType; + return mLetterboxBackgroundTypeOverride != LETTERBOX_BACKGROUND_OVERRIDE_UNSET + ? mLetterboxBackgroundTypeOverride + : getDefaultLetterboxBackgroundType(); } - /** Sets letterbox background type. */ - void setLetterboxBackgroundType(@LetterboxBackgroundType int backgroundType) { - mLetterboxBackgroundType = backgroundType; + /** Overrides the letterbox background type. */ + void setLetterboxBackgroundTypeOverride(@LetterboxBackgroundType int backgroundType) { + mLetterboxBackgroundTypeOverride = backgroundType; } /** - * Resets cletterbox background type to {@link - * com.android.internal.R.integer.config_letterboxBackgroundType}. + * Resets letterbox background type value depending on the + * {@link #KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER} built time and runtime flags. + * + * <p>If enabled, the letterbox background type value is set toZ + * {@link #LETTERBOX_BACKGROUND_WALLPAPER}. When disabled the letterbox background type value + * comes from {@link R.integer.config_letterboxBackgroundType}. */ void resetLetterboxBackgroundType() { - mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext); + mLetterboxBackgroundTypeOverride = LETTERBOX_BACKGROUND_OVERRIDE_UNSET; + } + + // Returns KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER if the DeviceConfig flag is enabled + // or the value in com.android.internal.R.integer.config_letterboxBackgroundType if the flag + // is disabled. + @LetterboxBackgroundType + private int getDefaultLetterboxBackgroundType() { + return mDeviceConfig.getFlagValue(KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER) + ? LETTERBOX_BACKGROUND_WALLPAPER : mLetterboxBackgroundType; } /** Returns a string representing the given {@link LetterboxBackgroundType}. */ @@ -548,7 +583,7 @@ final class LetterboxConfiguration { /** * Overrides alpha of a black scrim shown over wallpaper for {@link - * #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link mLetterboxBackgroundType}. + * #LETTERBOX_BACKGROUND_WALLPAPER} option returned from {@link getLetterboxBackgroundType()}. * * <p>If given value is < 0 or >= 1, both it and a value of {@link * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha} are ignored @@ -575,33 +610,33 @@ final class LetterboxConfiguration { } /** - * Overrides blur radius for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in - * {@link mLetterboxBackgroundType}. + * Overrides blur radius for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option from + * {@link getLetterboxBackgroundType()}. * * <p> If given value <= 0, both it and a value of {@link * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius} are ignored * and 0 is used instead. */ - void setLetterboxBackgroundWallpaperBlurRadius(int radius) { - mLetterboxBackgroundWallpaperBlurRadius = radius; + void setLetterboxBackgroundWallpaperBlurRadiusPx(int radius) { + mLetterboxBackgroundWallpaperBlurRadiusPx = radius; } /** - * Resets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link - * mLetterboxBackgroundType} to {@link + * Resets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option returned by {@link + * getLetterboxBackgroundType()} to {@link * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius}. */ - void resetLetterboxBackgroundWallpaperBlurRadius() { - mLetterboxBackgroundWallpaperBlurRadius = mContext.getResources().getDimensionPixelSize( + void resetLetterboxBackgroundWallpaperBlurRadiusPx() { + mLetterboxBackgroundWallpaperBlurRadiusPx = mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius); } /** - * Gets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link - * mLetterboxBackgroundType}. + * Gets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option returned by {@link + * getLetterboxBackgroundType()}. */ - int getLetterboxBackgroundWallpaperBlurRadius() { - return mLetterboxBackgroundWallpaperBlurRadius; + int getLetterboxBackgroundWallpaperBlurRadiusPx() { + return mLetterboxBackgroundWallpaperBlurRadiusPx; } /* diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index a81683829396..39f75703d71f 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -894,7 +894,7 @@ final class LetterboxUiController { this::shouldLetterboxHaveRoundedCorners, this::getLetterboxBackgroundColor, this::hasWallpaperBackgroundForLetterbox, - this::getLetterboxWallpaperBlurRadius, + this::getLetterboxWallpaperBlurRadiusPx, this::getLetterboxWallpaperDarkScrimAlpha, this::handleHorizontalDoubleTap, this::handleVerticalDoubleTap, @@ -1315,7 +1315,7 @@ final class LetterboxUiController { case LETTERBOX_BACKGROUND_WALLPAPER: if (hasWallpaperBackgroundForLetterbox()) { // Color is used for translucent scrim that dims wallpaper. - return Color.valueOf(Color.BLACK); + return mLetterboxConfiguration.getLetterboxBackgroundColor(); } Slog.w(TAG, "Wallpaper option is selected for letterbox background but " + "blur is not supported by a device or not supported in the current " @@ -1472,10 +1472,10 @@ final class LetterboxUiController { // Don't use wallpaper as a background if letterboxed for display cutout. && isLetterboxedNotForDisplayCutout(mainWindow) // Check that dark scrim alpha or blur radius are provided - && (getLetterboxWallpaperBlurRadius() > 0 + && (getLetterboxWallpaperBlurRadiusPx() > 0 || getLetterboxWallpaperDarkScrimAlpha() > 0) // Check that blur is supported by a device if blur radius is provided. - && (getLetterboxWallpaperBlurRadius() <= 0 + && (getLetterboxWallpaperBlurRadiusPx() <= 0 || isLetterboxWallpaperBlurSupported()); if (mShowWallpaperForLetterboxBackground != wallpaperShouldBeShown) { mShowWallpaperForLetterboxBackground = wallpaperShouldBeShown; @@ -1483,9 +1483,9 @@ final class LetterboxUiController { } } - private int getLetterboxWallpaperBlurRadius() { - int blurRadius = mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius(); - return blurRadius < 0 ? 0 : blurRadius; + private int getLetterboxWallpaperBlurRadiusPx() { + int blurRadius = mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadiusPx(); + return Math.max(blurRadius, 0); } private float getLetterboxWallpaperDarkScrimAlpha() { @@ -1535,7 +1535,7 @@ final class LetterboxUiController { pw.println(prefix + " letterboxBackgroundWallpaperDarkScrimAlpha=" + getLetterboxWallpaperDarkScrimAlpha()); pw.println(prefix + " letterboxBackgroundWallpaperBlurRadius=" - + getLetterboxWallpaperBlurRadius()); + + getLetterboxWallpaperBlurRadiusPx()); } pw.println(prefix + " isHorizontalReachabilityEnabled=" diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index 05e858de8973..f4781f9bc9f0 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -41,6 +41,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.util.DisplayMetrics; import android.util.Pair; +import android.util.TypedValue; import android.view.Display; import android.view.IWindow; import android.view.IWindowManager; @@ -728,7 +729,7 @@ public class WindowManagerShellCommand extends ShellCommand { return -1; } synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.setLetterboxBackgroundType(backgroundType); + mLetterboxConfiguration.setLetterboxBackgroundTypeOverride(backgroundType); } return 0; } @@ -770,10 +771,10 @@ public class WindowManagerShellCommand extends ShellCommand { private int runSetLetterboxBackgroundWallpaperBlurRadius(PrintWriter pw) throws RemoteException { - final int radius; + final int radiusDp; try { String arg = getNextArgRequired(); - radius = Integer.parseInt(arg); + radiusDp = Integer.parseInt(arg); } catch (NumberFormatException e) { getErrPrintWriter().println("Error: blur radius format " + e); return -1; @@ -783,7 +784,9 @@ public class WindowManagerShellCommand extends ShellCommand { return -1; } synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.setLetterboxBackgroundWallpaperBlurRadius(radius); + final int radiusPx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + radiusDp, mInternal.mContext.getResources().getDisplayMetrics()); + mLetterboxConfiguration.setLetterboxBackgroundWallpaperBlurRadiusPx(radiusPx); } return 0; } @@ -1050,7 +1053,7 @@ public class WindowManagerShellCommand extends ShellCommand { mLetterboxConfiguration.resetLetterboxBackgroundColor(); break; case "wallpaperBlurRadius": - mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius(); + mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadiusPx(); break; case "wallpaperDarkScrimAlpha": mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha(); @@ -1188,7 +1191,7 @@ public class WindowManagerShellCommand extends ShellCommand { mLetterboxConfiguration.resetLetterboxActivityCornersRadius(); mLetterboxConfiguration.resetLetterboxBackgroundType(); mLetterboxConfiguration.resetLetterboxBackgroundColor(); - mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius(); + mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadiusPx(); mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha(); mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier(); mLetterboxConfiguration.resetIsHorizontalReachabilityEnabled(); @@ -1262,7 +1265,7 @@ public class WindowManagerShellCommand extends ShellCommand { pw.println(" Background color: " + Integer.toHexString( mLetterboxConfiguration.getLetterboxBackgroundColor().toArgb())); pw.println(" Wallpaper blur radius: " - + mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius()); + + mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadiusPx()); pw.println(" Wallpaper dark scrim alpha: " + mLetterboxConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha()); pw.println("Is letterboxing for translucent activities enabled: " diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java index a1199d99f1c3..6747cea80115 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java @@ -91,8 +91,6 @@ public final class CredentialManagerService CredentialManagerService, CredentialManagerServiceImpl> { private static final String TAG = "CredManSysService"; - private static final String DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API = - "enable_credential_description_api"; private static final String PERMISSION_DENIED_ERROR = "permission_denied"; private static final String PERMISSION_DENIED_WRITE_SECURE_SETTINGS_ERROR = "Caller is missing WRITE_SECURE_SETTINGS permission"; @@ -311,14 +309,7 @@ public final class CredentialManagerService } public static boolean isCredentialDescriptionApiEnabled() { - final long origId = Binder.clearCallingIdentity(); - try { - return DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API, - false); - } finally { - Binder.restoreCallingIdentity(origId); - } + return true; } @SuppressWarnings("GuardedBy") // ErrorProne requires initiateProviderSessionForRequestLocked diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java index 0ed863e3743e..ffd7332896dc 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java @@ -126,6 +126,7 @@ public class InputMethodManagerServiceTestBase { protected InputMethodManagerService mInputMethodManagerService; protected ServiceThread mServiceThread; protected boolean mIsLargeScreen; + private InputManagerGlobal.TestSession mInputManagerGlobalSession; @BeforeClass public static void setupClass() { @@ -186,7 +187,7 @@ public class InputMethodManagerServiceTestBase { // Injecting and mocked InputMethodBindingController and InputMethod. mMockInputMethodInvoker = IInputMethodInvoker.create(mMockInputMethod); - InputManagerGlobal.resetInstance(mMockIInputManager); + mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mMockIInputManager); synchronized (ImfLock.class) { when(mMockInputMethodBindingController.getCurMethod()) .thenReturn(mMockInputMethodInvoker); @@ -262,6 +263,10 @@ public class InputMethodManagerServiceTestBase { if (mMockingSession != null) { mMockingSession.finishMocking(); } + + if (mInputManagerGlobalSession != null) { + mInputManagerGlobalSession.close(); + } LocalServices.removeServiceForTest(InputMethodManagerInternal.class); } diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java index 6c6b60803ed3..7e6883bcec63 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java @@ -44,6 +44,7 @@ import androidx.test.InstrumentationRegistry; import com.android.server.LocalServices; import com.android.server.input.InputManagerInternal; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -93,6 +94,11 @@ public class InputControllerTest { threadVerifier); } + @After + public void tearDown() { + mInputManagerMockHelper.tearDown(); + } + @Test public void registerInputDevice_deviceCreation_hasDeviceId() { final IBinder device1Token = new Binder("device1"); diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java index 5cadc0a5d036..3722247566d9 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java @@ -46,6 +46,7 @@ class InputManagerMockHelper { private final TestableLooper mTestableLooper; private final InputController.NativeWrapper mNativeWrapperMock; private final IInputManager mIInputManagerMock; + private final InputManagerGlobal.TestSession mInputManagerGlobalSession; private final List<InputDevice> mDevices = new ArrayList<>(); private IInputDevicesChangedListener mDevicesChangedListener; @@ -77,7 +78,13 @@ class InputManagerMockHelper { // Set a new instance of InputManager for testing that uses the IInputManager mock as the // interface to the server. - InputManagerGlobal.resetInstance(mIInputManagerMock); + mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock); + } + + public void tearDown() { + if (mInputManagerGlobalSession != null) { + mInputManagerGlobalSession.close(); + } } private long handleNativeOpenInputDevice(InvocationOnMock inv) { diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java index 4b801bc26254..d76d615cc36a 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java @@ -376,6 +376,7 @@ public class VirtualDeviceManagerServiceTest { @After public void tearDown() { mDeviceImpl.close(); + mInputManagerMockHelper.tearDown(); } @Test 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 ad1c60e2cc8a..7478778182bd 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -46,7 +46,6 @@ import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_ENTERPRISE_EAP import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_OPEN; import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_PERSONAL; import static android.app.admin.DevicePolicyManager.WIPE_EUICC; -import static android.app.admin.PasswordMetrics.computeForPasswordOrPin; import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE; import static android.location.LocationManager.FUSED_PROVIDER; import static android.location.LocationManager.GPS_PROVIDER; @@ -5479,8 +5478,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { reset(mContext.spiedContext); - PasswordMetrics passwordMetricsNoSymbols = computeForPasswordOrPin( - "abcdXYZ5".getBytes(), /* isPin */ false); + PasswordMetrics passwordMetricsNoSymbols = metricsForPassword("abcdXYZ5"); setActivePasswordState(passwordMetricsNoSymbols); assertThat(dpm.isActivePasswordSufficient()).isTrue(); @@ -5507,8 +5505,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { reset(mContext.spiedContext); assertThat(dpm.isActivePasswordSufficient()).isFalse(); - PasswordMetrics passwordMetricsWithSymbols = computeForPasswordOrPin( - "abcd.XY5".getBytes(), /* isPin */ false); + PasswordMetrics passwordMetricsWithSymbols = metricsForPassword("abcd.XY5"); setActivePasswordState(passwordMetricsWithSymbols); assertThat(dpm.isActivePasswordSufficient()).isTrue(); @@ -5564,7 +5561,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { parentDpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_MEDIUM); when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) - .thenReturn(computeForPasswordOrPin("184342".getBytes(), /* isPin */ true)); + .thenReturn(metricsForPin("184342")); // Numeric password is compliant with current requirement (QUALITY_NUMERIC set explicitly // on the parent admin) @@ -5685,7 +5682,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set a work challenge and verify profile.isActivePasswordSufficient is now true when(getServices().lockSettingsInternal.getUserPasswordMetrics(managedProfileUserId)) - .thenReturn(computeForPasswordOrPin("abcdXYZ5".getBytes(), /* isPin */ false)); + .thenReturn(metricsForPassword("abcdXYZ5")); assertThat(dpm.isActivePasswordSufficient()).isTrue(); assertThat(parentDpm.isActivePasswordSufficient()).isTrue(); } @@ -5710,7 +5707,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set a work challenge and verify profile.isActivePasswordSufficient is now true when(getServices().lockSettingsInternal.getUserPasswordMetrics(managedProfileUserId)) - .thenReturn(computeForPasswordOrPin("5156".getBytes(), /* isPin */ true)); + .thenReturn(metricsForPin("5156")); assertThat(dpm.isActivePasswordSufficient()).isTrue(); assertThat(parentDpm.isActivePasswordSufficient()).isTrue(); } @@ -5735,7 +5732,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set a device lockscreen and verify parent.isActivePasswordSufficient is now true when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) - .thenReturn(computeForPasswordOrPin("acbdXYZ5".getBytes(), /* isPin */ false)); + .thenReturn(metricsForPassword("acbdXYZ5")); assertThat(dpm.isActivePasswordSufficient()).isTrue(); assertThat(parentDpm.isActivePasswordSufficient()).isTrue(); } @@ -5758,7 +5755,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set a device lockscreen and verify parent.isActivePasswordSufficient is now true when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) - .thenReturn(computeForPasswordOrPin("1234".getBytes(), /* isPin */ true)); + .thenReturn(metricsForPin("1234")); assertThat(dpm.isActivePasswordSufficient()).isTrue(); assertThat(parentDpm.isActivePasswordSufficient()).isTrue(); } @@ -5783,7 +5780,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set a device lockscreen and verify {profile, parent}.isActivePasswordSufficient is true when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) - .thenReturn(computeForPasswordOrPin("abcdXYZ5".getBytes(), /* isPin */ false)); + .thenReturn(metricsForPassword("abcdXYZ5")); assertThat(dpm.isActivePasswordSufficient()).isTrue(); assertThat(parentDpm.isActivePasswordSufficient()).isTrue(); } @@ -5806,7 +5803,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set a device lockscreen and verify {profile, parent}.isActivePasswordSufficient is true when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) - .thenReturn(computeForPasswordOrPin("51567548".getBytes(), /* isPin */ true)); + .thenReturn(metricsForPin("51567548")); assertThat(dpm.isActivePasswordSufficient()).isTrue(); assertThat(parentDpm.isActivePasswordSufficient()).isTrue(); } @@ -5831,7 +5828,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set a device lockscreen and verify {profile, parent}.isActivePasswordSufficient is true when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) - .thenReturn(computeForPasswordOrPin("abcdXYZ5".getBytes(), /* isPin */ false)); + .thenReturn(metricsForPassword("abcdXYZ5")); assertThat(dpm.isActivePasswordSufficient()).isTrue(); assertThat(parentDpm.isActivePasswordSufficient()).isTrue(); } @@ -5854,7 +5851,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set a device lockscreen and verify {profile, parent}.isActivePasswordSufficient is true when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) - .thenReturn(computeForPasswordOrPin("5156".getBytes(), /* isPin */ true)); + .thenReturn(metricsForPin("5156")); assertThat(dpm.isActivePasswordSufficient()).isTrue(); assertThat(parentDpm.isActivePasswordSufficient()).isTrue(); } @@ -6909,7 +6906,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { .thenReturn(CALLER_USER_HANDLE); when(getServices().lockSettingsInternal .getUserPasswordMetrics(CALLER_USER_HANDLE)) - .thenReturn(computeForPasswordOrPin("asdf".getBytes(), /* isPin */ false)); + .thenReturn(metricsForPassword("asdf")); assertThat(dpm.getPasswordComplexity()).isEqualTo(PASSWORD_COMPLEXITY_MEDIUM); } @@ -6929,10 +6926,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { when(getServices().lockSettingsInternal .getUserPasswordMetrics(CALLER_USER_HANDLE)) - .thenReturn(computeForPasswordOrPin("asdf".getBytes(), /* isPin */ false)); + .thenReturn(metricsForPassword("asdf")); when(getServices().lockSettingsInternal .getUserPasswordMetrics(parentUser.id)) - .thenReturn(computeForPasswordOrPin("parentUser".getBytes(), /* isPin */ false)); + .thenReturn(metricsForPassword("parentUser")); assertThat(dpm.getPasswordComplexity()).isEqualTo(PASSWORD_COMPLEXITY_HIGH); } @@ -7654,15 +7651,13 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertThat(dpm.getPasswordComplexity()).isEqualTo(PASSWORD_COMPLEXITY_NONE); reset(mContext.spiedContext); - PasswordMetrics passwordMetricsNoSymbols = computeForPasswordOrPin( - "1234".getBytes(), /* isPin */ true); + PasswordMetrics passwordMetricsNoSymbols = metricsForPin("1234"); setActivePasswordState(passwordMetricsNoSymbols); assertThat(dpm.getPasswordComplexity()).isEqualTo(PASSWORD_COMPLEXITY_LOW); assertThat(dpm.isActivePasswordSufficient()).isFalse(); reset(mContext.spiedContext); - passwordMetricsNoSymbols = computeForPasswordOrPin( - "84125312943a".getBytes(), /* isPin */ false); + passwordMetricsNoSymbols = metricsForPassword("84125312943a"); setActivePasswordState(passwordMetricsNoSymbols); assertThat(dpm.getPasswordComplexity()).isEqualTo(PASSWORD_COMPLEXITY_HIGH); // using isActivePasswordSufficient @@ -8837,4 +8832,12 @@ public class DevicePolicyManagerTest extends DpmTestBase { assumeTrue("device doesn't support deprecated password APIs", isDeprecatedPasswordApisSupported()); } + + private static PasswordMetrics metricsForPassword(String password) { + return PasswordMetrics.computeForCredential(LockscreenCredential.createPassword(password)); + } + + private static PasswordMetrics metricsForPin(String pin) { + return PasswordMetrics.computeForCredential(LockscreenCredential.createPin(pin)); + } } diff --git a/services/tests/servicestests/src/com/android/server/input/OWNERS b/services/tests/servicestests/src/com/android/server/input/OWNERS deleted file mode 100644 index 6e9aa1dee696..000000000000 --- a/services/tests/servicestests/src/com/android/server/input/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -include /services/core/java/com/android/server/input/OWNERS - diff --git a/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java b/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java index c9f00d767d04..d9b698e92327 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java @@ -24,6 +24,7 @@ import static com.google.common.truth.Truth.assertWithMessage; import android.app.ActivityManager; import android.app.IStopUserCallback; import android.content.Context; +import android.content.Intent; import android.content.pm.UserInfo; import android.os.RemoteException; import android.os.UserManager; @@ -35,6 +36,8 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; +import com.android.compatibility.common.util.BlockingBroadcastReceiver; +import com.android.compatibility.common.util.ShellUtils; import com.android.internal.util.FunctionalUtils; import org.junit.After; @@ -46,6 +49,7 @@ import java.io.IOException; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; /** * To run the test: @@ -58,9 +62,14 @@ public class UserLifecycleStressTest { private static final String TAG = "UserLifecycleStressTest"; // TODO: Make this smaller once we have improved it. private static final int TIMEOUT_IN_SECOND = 40; + private static final int CHECK_USER_REMOVED_INTERVAL_MS = 200; + private static final int NUM_ITERATIONS = 8; private static final int WAIT_BEFORE_STOP_USER_IN_SECOND = 3; + /** Name of users/profiles in the test. Users with this name may be freely removed. */ + private static final String TEST_USER_NAME = "UserLifecycleStressTest_test_user"; + private Context mContext; private UserManager mUserManager; private ActivityManager mActivityManager; @@ -75,6 +84,7 @@ public class UserLifecycleStressTest { mUserSwitchWaiter = new UserSwitchWaiter(TAG, TIMEOUT_IN_SECOND); mRemoveGuestOnExitOriginalValue = Settings.Global.getString(mContext.getContentResolver(), Settings.Global.REMOVE_GUEST_ON_EXIT); + waitForBroadcastBarrier(); // isolate tests from each other } @After @@ -82,6 +92,7 @@ public class UserLifecycleStressTest { mUserSwitchWaiter.close(); Settings.Global.putString(mContext.getContentResolver(), Settings.Global.REMOVE_GUEST_ON_EXIT, mRemoveGuestOnExitOriginalValue); + waitForBroadcastBarrier(); // isolate tests from each other } /** @@ -91,8 +102,10 @@ public class UserLifecycleStressTest { @Test public void stopManagedProfileStressTest() throws RemoteException, InterruptedException { for (int i = 0; i < NUM_ITERATIONS; i++) { - final UserInfo userInfo = mUserManager.createProfileForUser("TestUser", - UserManager.USER_TYPE_PROFILE_MANAGED, 0, mActivityManager.getCurrentUser()); + logIteration(i, "stopManagedProfileStressTest"); + + final UserInfo userInfo = mUserManager.createProfileForUser(TEST_USER_NAME, + UserManager.USER_TYPE_PROFILE_MANAGED, 0, ActivityManager.getCurrentUser()); assertThat(userInfo).isNotNull(); try { assertWithMessage("Failed to start the profile") @@ -109,6 +122,35 @@ public class UserLifecycleStressTest { } /** + * Create a user, and then remove it immediately after starting it in background + * {@link #NUM_ITERATIONS} times in a row. + * Check device is not crashed when user data directory is deleted while some other processes + * might still be trying to access those deleted files. + */ + @Test + public void removeRecentlyStartedUserStressTest() throws RemoteException, InterruptedException { + for (int i = 0; i < NUM_ITERATIONS; i++) { + logIteration(i, "removeRecentlyStartedUserStressTest"); + + Log.d(TAG, "Creating a new user"); + final UserInfo userInfo = mUserManager.createUser(TEST_USER_NAME, + UserManager.USER_TYPE_FULL_SECONDARY, 0); + assertWithMessage("Failed to create the user") + .that(userInfo) + .isNotNull(); + try { + Log.d(TAG, "Starting user " + userInfo.id); + startUserInBackgroundAndWaitForUserStartedBroadcast(userInfo.id); + } finally { + Log.d(TAG, "Removing user " + userInfo.id); + assertWithMessage("Failed to remove the user " + userInfo.id) + .that(removeUser(userInfo.id)) + .isTrue(); + } + } + } + + /** * Starts over the guest user {@link #NUM_ITERATIONS} times in a row. * * Starting over the guest means the following: @@ -130,10 +172,9 @@ public class UserLifecycleStressTest { int nextGuestId = guestUsers.isEmpty() ? USER_NULL : guestUsers.get(0).id; for (int i = 0; i < NUM_ITERATIONS; i++) { - final int currentGuestId = nextGuestId; + logIteration(i, "switchToExistingGuestAndStartOverStressTest"); - Log.d(TAG, "switchToExistingGuestAndStartOverStressTest" - + " - Run " + (i + 1) + " / " + NUM_ITERATIONS); + final int currentGuestId = nextGuestId; if (currentGuestId != USER_NULL) { Log.d(TAG, "Switching to the existing guest"); @@ -173,6 +214,25 @@ public class UserLifecycleStressTest { Log.d(TAG, "testSwitchToExistingGuestAndStartOver - End"); } + private boolean removeUser(int userId) { + if (!mUserManager.removeUser(userId)) { + return false; + } + try { + final long startTime = System.currentTimeMillis(); + final long timeoutInMs = TIMEOUT_IN_SECOND * 1000; + while (mUserManager.getUserInfo(userId) != null + && System.currentTimeMillis() - startTime < timeoutInMs) { + TimeUnit.MILLISECONDS.sleep(CHECK_USER_REMOVED_INTERVAL_MS); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (Exception e) { + // Ignore + } + return mUserManager.getUserInfo(userId) == null; + } + /** Stops the given user and waits for the stop to finish. */ private void stopUser(int userId) throws RemoteException, InterruptedException { runWithLatch("stop user", countDownLatch -> { @@ -204,6 +264,40 @@ public class UserLifecycleStressTest { } /** + * Start user in background and wait for {@link Intent#ACTION_USER_STARTED} broadcast. + * <p> To start in foreground instead, see {@link #switchUser(int)}. + * <p> This should always be used for profiles since profiles cannot be started in foreground. + */ + private void startUserInBackgroundAndWaitForUserStartedBroadcast(int userId) { + runWithBlockingBroadcastReceiver("start user and wait for ACTION_USER_STARTED broadcast", + userId, Intent.ACTION_USER_STARTED, + () -> ActivityManager.getService().startUserInBackground(userId)); + } + + /** + * Calls the given runnable and expects the given broadcast to be received before timeout, + * or fails the test otherwise. + * @param tag tag for logging + * @param userId id of the user to register the broadcast receiver with + * see {@link Context#registerReceiverAsUser} + * @param action action of the broadcast intent filter i.e. {@link Intent#ACTION_USER_STARTED} + * @param runnable this will be called after registering the broadcast receiver + */ + private void runWithBlockingBroadcastReceiver(String tag, int userId, String action, + FunctionalUtils.ThrowingRunnable runnable) { + try (BlockingBroadcastReceiver blockingBroadcastReceiver = new BlockingBroadcastReceiver( + mContext, action, + intent -> intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL) == userId)) { + blockingBroadcastReceiver.setTimeout(TIMEOUT_IN_SECOND); + blockingBroadcastReceiver.registerForAllUsers(); + runnable.run(); + assertWithMessage("Took more than " + TIMEOUT_IN_SECOND + "s to " + tag) + .that(blockingBroadcastReceiver.awaitForBroadcast()) + .isNotNull(); + } + } + + /** * Calls the given consumer with a CountDownLatch parameter, and expects it's countDown() method * to be called before timeout, or fails the test otherwise. */ @@ -222,5 +316,20 @@ public class UserLifecycleStressTest { final long elapsedTime = System.currentTimeMillis() - startTime; Log.d(TAG, tag + " takes " + elapsedTime + " ms"); } + + private void logIteration(int iteration, String testMethodName) { + Log.d(TAG, testMethodName + " - Iteration " + (iteration + 1) + " / " + NUM_ITERATIONS); + } + + private static void waitForBroadcastBarrier() { + try { + Log.d(TAG, "Starting to waitForBroadcastBarrier"); + ShellUtils.runShellCommandWithTimeout("am wait-for-broadcast-barrier", + TIMEOUT_IN_SECOND); + Log.d(TAG, "waitForBroadcastBarrier is finished"); + } catch (TimeoutException e) { + Log.e(TAG, "Timeout while running waitForBroadcastBarrier", e); + } + } } diff --git a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java index 2ab79fcdeae7..f3c17a84cdc2 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java @@ -77,6 +77,7 @@ public class InputDeviceDelegateTest { private TestLooper mTestLooper; private ContextWrapper mContextSpy; + private InputManagerGlobal.TestSession mInputManagerGlobalSession; private InputManager mInputManager; private InputDeviceDelegate mInputDeviceDelegate; private IInputDevicesChangedListener mIInputDevicesChangedListener; @@ -84,7 +85,7 @@ public class InputDeviceDelegateTest { @Before public void setUp() throws Exception { mTestLooper = new TestLooper(); - InputManagerGlobal.resetInstance(mIInputManagerMock); + mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock); mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); mInputManager = new InputManager(mContextSpy); @@ -100,7 +101,9 @@ public class InputDeviceDelegateTest { @After public void tearDown() throws Exception { - InputManagerGlobal.clearInstance(); + if (mInputManagerGlobalSession != null) { + mInputManagerGlobalSession.close(); + } } @Test diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java index c805fc5bb4d0..9a911f40392d 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java @@ -186,14 +186,14 @@ public class VibratorManagerServiceTest { private VirtualDeviceManagerInternal.VirtualDisplayListener mRegisteredVirtualDisplayListener; private VirtualDeviceManagerInternal.AppsOnVirtualDeviceListener mRegisteredAppsOnVirtualDeviceListener; - + private InputManagerGlobal.TestSession mInputManagerGlobalSession; private InputManager mInputManager; @Before public void setUp() throws Exception { mTestLooper = new TestLooper(); mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); - InputManagerGlobal.resetInstance(mIInputManagerMock); + mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock); mVibrationConfig = new VibrationConfig(mContextSpy.getResources()); ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); @@ -268,7 +268,9 @@ public class VibratorManagerServiceTest { LocalServices.removeServiceForTest(PowerManagerInternal.class); // Ignore potential exceptions about the looper having never dispatched any messages. mTestLooper.stopAutoDispatchAndIgnoreExceptions(); - InputManagerGlobal.clearInstance(); + if (mInputManagerGlobalSession != null) { + mInputManagerGlobalSession.close(); + } } private VibratorManagerService createSystemReadyService() { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 30180e8f43a7..7e81ef22eb65 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -11269,7 +11269,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // Given: a call notification has the flag FLAG_ONGOING_EVENT set // feature flag: ALLOW_DISMISS_ONGOING is on mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true); - when(mTelecomManager.isInManagedCall()).thenReturn(true); Person person = new Person.Builder() .setName("caller") diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index 5c1b262a9dc8..45331f76fa4c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -1036,7 +1036,7 @@ public class WindowManagerServiceTests extends WindowTestsBase { private boolean setupLetterboxConfigurationWithBackgroundType( @LetterboxConfiguration.LetterboxBackgroundType int letterboxBackgroundType) { - mWm.mLetterboxConfiguration.setLetterboxBackgroundType(letterboxBackgroundType); + mWm.mLetterboxConfiguration.setLetterboxBackgroundTypeOverride(letterboxBackgroundType); return mWm.isLetterboxBackgroundMultiColored(); } } diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp index 4fa6fbe1d4e1..96b685dc356a 100644 --- a/tests/Input/Android.bp +++ b/tests/Input/Android.bp @@ -19,13 +19,26 @@ android_test { platform_apis: true, certificate: "platform", static_libs: [ + "androidx.test.core", "androidx.test.ext.junit", + "androidx.test.ext.truth", "androidx.test.rules", + "androidx.test.runner", + "androidx.test.uiautomator_uiautomator", + "servicestests-utils", + "frameworks-base-testutils", + "hamcrest-library", + "kotlin-test", "mockito-target-minus-junit4", + "platform-test-annotations", "services.core.unboosted", "testables", + "testng", "truth-prebuilt", - "androidx.test.uiautomator_uiautomator", + ], + libs: [ + "android.test.mock", + "android.test.base", ], test_suites: ["device-tests"], } diff --git a/tests/Input/AndroidManifest.xml b/tests/Input/AndroidManifest.xml index 20f564e80b3d..3b723ddf811f 100644 --- a/tests/Input/AndroidManifest.xml +++ b/tests/Input/AndroidManifest.xml @@ -16,11 +16,14 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.test.input"> + + <uses-permission android:name="android.permission.INJECT_EVENTS"/> + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/> <uses-permission android:name="android.permission.MONITOR_INPUT"/> + <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/> - <uses-permission android:name="android.permission.INJECT_EVENTS"/> - <application android:label="InputTest"> + <application android:label="InputTest" android:debuggable="true"> <activity android:name=".UnresponsiveGestureMonitorActivity" android:label="Unresponsive gesture monitor" diff --git a/services/tests/servicestests/res/raw/dummy_keyboard_layout.kcm b/tests/Input/res/raw/dummy_keyboard_layout.kcm index ea6bc980b7b6..ea6bc980b7b6 100644 --- a/services/tests/servicestests/res/raw/dummy_keyboard_layout.kcm +++ b/tests/Input/res/raw/dummy_keyboard_layout.kcm diff --git a/services/tests/servicestests/res/raw/input_port_associations.xml b/tests/Input/res/raw/input_port_associations.xml index b10d541f942c..b10d541f942c 100644 --- a/services/tests/servicestests/res/raw/input_port_associations.xml +++ b/tests/Input/res/raw/input_port_associations.xml diff --git a/services/tests/servicestests/res/raw/input_port_associations_bad_displayport.xml b/tests/Input/res/raw/input_port_associations_bad_displayport.xml index 8eeb1f58ef9e..8eeb1f58ef9e 100644 --- a/services/tests/servicestests/res/raw/input_port_associations_bad_displayport.xml +++ b/tests/Input/res/raw/input_port_associations_bad_displayport.xml diff --git a/services/tests/servicestests/res/raw/input_port_associations_bad_xml.xml b/tests/Input/res/raw/input_port_associations_bad_xml.xml index cf6e12486239..cf6e12486239 100644 --- a/services/tests/servicestests/res/raw/input_port_associations_bad_xml.xml +++ b/tests/Input/res/raw/input_port_associations_bad_xml.xml diff --git a/services/tests/servicestests/res/xml/keyboard_layouts.xml b/tests/Input/res/xml/keyboard_layouts.xml index 5f3fcd6eaed0..5f3fcd6eaed0 100644 --- a/services/tests/servicestests/res/xml/keyboard_layouts.xml +++ b/tests/Input/res/xml/keyboard_layouts.xml diff --git a/core/tests/coretests/src/android/hardware/input/InputDeviceBatteryListenerTest.kt b/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt index fdcc7c993ddd..90dff47ab706 100644 --- a/core/tests/coretests/src/android/hardware/input/InputDeviceBatteryListenerTest.kt +++ b/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt @@ -46,7 +46,7 @@ import org.mockito.junit.MockitoJUnitRunner * Tests for [InputManager.InputDeviceBatteryListener]. * * Build/Install/Run: - * atest FrameworksCoreTests:InputDeviceBatteryListenerTest + * atest InputTests:InputDeviceBatteryListenerTest */ @Presubmit @RunWith(MockitoJUnitRunner::class) @@ -63,6 +63,7 @@ class InputDeviceBatteryListenerTest { @Mock private lateinit var iInputManagerMock: IInputManager + private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession @Before fun setUp() { @@ -71,7 +72,7 @@ class InputDeviceBatteryListenerTest { executor = HandlerExecutor(Handler(testLooper.looper)) registeredListener = null monitoredDevices.clear() - InputManagerGlobal.resetInstance(iInputManagerMock) + inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock) inputManager = InputManager(context) `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))) .thenReturn(inputManager) @@ -112,7 +113,9 @@ class InputDeviceBatteryListenerTest { @After fun tearDown() { - InputManagerGlobal.clearInstance() + if (this::inputManagerGlobalSession.isInitialized) { + inputManagerGlobalSession.close() + } } private fun notifyBatteryStateChanged( diff --git a/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java b/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java index 1e505abbc169..080186e4a2c1 100644 --- a/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java +++ b/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java @@ -43,7 +43,7 @@ import android.platform.test.annotations.Presubmit; import android.util.ArrayMap; import android.view.InputDevice; -import androidx.test.InstrumentationRegistry; +import androidx.test.platform.app.InstrumentationRegistry; import org.junit.After; import org.junit.Before; @@ -63,7 +63,7 @@ import java.util.List; * Tests for {@link InputDeviceLightsManager}. * * Build/Install/Run: - * atest FrameworksCoreTests:InputDeviceLightsManagerTest + * atest InputTests:InputDeviceLightsManagerTest */ @Presubmit @RunWith(MockitoJUnitRunner.class) @@ -78,16 +78,18 @@ public class InputDeviceLightsManagerTest { private InputManager mInputManager; @Mock private IInputManager mIInputManagerMock; + private InputManagerGlobal.TestSession mInputManagerGlobalSession; @Before public void setUp() throws Exception { - final Context context = spy(new ContextWrapper(InstrumentationRegistry.getContext())); + final Context context = spy( + new ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext())); when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID}); when(mIInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn( createInputDevice(DEVICE_ID)); - InputManagerGlobal.resetInstance(mIInputManagerMock); + mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock); mInputManager = new InputManager(context); when(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(mInputManager); @@ -114,7 +116,9 @@ public class InputDeviceLightsManagerTest { @After public void tearDown() { - InputManagerGlobal.clearInstance(); + if (mInputManagerGlobalSession != null) { + mInputManagerGlobalSession.close(); + } } private InputDevice createInputDevice(int id) { diff --git a/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java b/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java index b33cfdda4a09..0e3c200699d2 100644 --- a/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java +++ b/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java @@ -38,7 +38,7 @@ import android.hardware.SensorManager; import android.platform.test.annotations.Presubmit; import android.view.InputDevice; -import androidx.test.InstrumentationRegistry; +import androidx.test.platform.app.InstrumentationRegistry; import com.android.internal.annotations.GuardedBy; @@ -61,7 +61,7 @@ import java.util.concurrent.TimeUnit; * Tests for {@link InputDeviceSensorManager}. * * Build/Install/Run: - * atest FrameworksCoreTests:InputDeviceSensorManagerTest + * atest InputTests:InputDeviceSensorManagerTest */ @Presubmit @RunWith(MockitoJUnitRunner.class) @@ -77,11 +77,13 @@ public class InputDeviceSensorManagerTest { private final Object mLock = new Object(); @Mock private IInputManager mIInputManagerMock; + private InputManagerGlobal.TestSession mInputManagerGlobalSession; @Before public void setUp() throws Exception { - final Context context = spy(new ContextWrapper(InstrumentationRegistry.getContext())); - InputManagerGlobal.resetInstance(mIInputManagerMock); + final Context context = spy( + new ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext())); + mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock); mInputManager = new InputManager(context); when(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(mInputManager); @@ -102,7 +104,9 @@ public class InputDeviceSensorManagerTest { @After public void tearDown() { - InputManagerGlobal.clearInstance(); + if (mInputManagerGlobalSession != null) { + mInputManagerGlobalSession.close(); + } } private class InputTestSensorEventListener implements SensorEventListener { diff --git a/core/tests/coretests/src/android/hardware/input/InputManagerTest.kt b/tests/Input/src/android/hardware/input/InputManagerTest.kt index 2ebe362b60f2..152dde94f006 100644 --- a/core/tests/coretests/src/android/hardware/input/InputManagerTest.kt +++ b/tests/Input/src/android/hardware/input/InputManagerTest.kt @@ -41,7 +41,7 @@ import org.mockito.junit.MockitoJUnitRunner * Tests for [InputManager]. * * Build/Install/Run: - * atest FrameworksCoreTests:InputManagerTest + * atest InputTests:InputManagerTest */ @Presubmit @RunWith(MockitoJUnitRunner::class) @@ -63,11 +63,12 @@ class InputManagerTest { @Mock private lateinit var iInputManager: IInputManager + private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession @Before fun setUp() { context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext())) - InputManagerGlobal.resetInstance(iInputManager) + inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager) inputManager = InputManager(context) `when`(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager) `when`(iInputManager.inputDeviceIds).then { @@ -77,7 +78,9 @@ class InputManagerTest { @After fun tearDown() { - InputManagerGlobal.clearInstance() + if (this::inputManagerGlobalSession.isInitialized) { + inputManagerGlobalSession.close() + } } private fun notifyDeviceChanged( diff --git a/core/tests/coretests/src/android/hardware/input/KeyboardBacklightListenerTest.kt b/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt index ce816ab98b12..23135b2550b0 100644 --- a/core/tests/coretests/src/android/hardware/input/KeyboardBacklightListenerTest.kt +++ b/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt @@ -45,7 +45,7 @@ import kotlin.test.fail * Tests for [InputManager.KeyboardBacklightListener]. * * Build/Install/Run: - * atest FrameworksCoreTests:KeyboardBacklightListenerTest + * atest InputTests:KeyboardBacklightListenerTest */ @Presubmit @RunWith(MockitoJUnitRunner::class) @@ -58,6 +58,7 @@ class KeyboardBacklightListenerTest { private lateinit var executor: Executor private lateinit var context: Context private lateinit var inputManager: InputManager + private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession @Mock private lateinit var iInputManagerMock: IInputManager @@ -65,7 +66,7 @@ class KeyboardBacklightListenerTest { @Before fun setUp() { context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext())) - InputManagerGlobal.resetInstance(iInputManagerMock) + inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock) testLooper = TestLooper() executor = HandlerExecutor(Handler(testLooper.looper)) registeredListener = null @@ -99,7 +100,9 @@ class KeyboardBacklightListenerTest { @After fun tearDown() { - InputManagerGlobal.clearInstance() + if (this::inputManagerGlobalSession.isInitialized) { + inputManagerGlobalSession.close() + } } private fun notifyKeyboardBacklightChanged( diff --git a/core/tests/coretests/src/android/hardware/input/VirtualKeyEventTest.java b/tests/Input/src/android/hardware/input/VirtualKeyEventTest.java index 37cc9b70dd1b..37cc9b70dd1b 100644 --- a/core/tests/coretests/src/android/hardware/input/VirtualKeyEventTest.java +++ b/tests/Input/src/android/hardware/input/VirtualKeyEventTest.java diff --git a/core/tests/coretests/src/android/hardware/input/VirtualMouseButtonEventTest.java b/tests/Input/src/android/hardware/input/VirtualMouseButtonEventTest.java index 789e0bb2ff56..789e0bb2ff56 100644 --- a/core/tests/coretests/src/android/hardware/input/VirtualMouseButtonEventTest.java +++ b/tests/Input/src/android/hardware/input/VirtualMouseButtonEventTest.java diff --git a/core/tests/coretests/src/android/hardware/input/VirtualMouseRelativeEventTest.java b/tests/Input/src/android/hardware/input/VirtualMouseRelativeEventTest.java index c0508162869b..c0508162869b 100644 --- a/core/tests/coretests/src/android/hardware/input/VirtualMouseRelativeEventTest.java +++ b/tests/Input/src/android/hardware/input/VirtualMouseRelativeEventTest.java diff --git a/core/tests/coretests/src/android/hardware/input/VirtualMouseScrollEventTest.java b/tests/Input/src/android/hardware/input/VirtualMouseScrollEventTest.java index 2259c740da7e..2259c740da7e 100644 --- a/core/tests/coretests/src/android/hardware/input/VirtualMouseScrollEventTest.java +++ b/tests/Input/src/android/hardware/input/VirtualMouseScrollEventTest.java diff --git a/core/tests/coretests/src/android/hardware/input/VirtualTouchEventTest.java b/tests/Input/src/android/hardware/input/VirtualTouchEventTest.java index 100aba5ab362..100aba5ab362 100644 --- a/core/tests/coretests/src/android/hardware/input/VirtualTouchEventTest.java +++ b/tests/Input/src/android/hardware/input/VirtualTouchEventTest.java diff --git a/services/tests/servicestests/src/com/android/server/input/AmbientKeyboardBacklightControllerTests.kt b/tests/Input/src/com/android/server/input/AmbientKeyboardBacklightControllerTests.kt index 98b46286e977..ad481dff810c 100644 --- a/services/tests/servicestests/src/com/android/server/input/AmbientKeyboardBacklightControllerTests.kt +++ b/tests/Input/src/com/android/server/input/AmbientKeyboardBacklightControllerTests.kt @@ -55,7 +55,7 @@ import org.mockito.junit.MockitoJUnit * Tests for {@link AmbientKeyboardBacklightController}. * * Build/Install/Run: - * atest FrameworksServicesTests:AmbientKeyboardBacklightControllerTests + * atest InputTests:AmbientKeyboardBacklightControllerTests */ @Presubmit class AmbientKeyboardBacklightControllerTests { diff --git a/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt b/tests/Input/src/com/android/server/input/BatteryControllerTests.kt index c36122b7e788..f2724e605553 100644 --- a/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt +++ b/tests/Input/src/com/android/server/input/BatteryControllerTests.kt @@ -167,7 +167,7 @@ private fun createMockListener(): IInputDeviceBatteryListener { * Tests for {@link InputDeviceBatteryController}. * * Build/Install/Run: - * atest FrameworksServicesTests:InputDeviceBatteryControllerTests + * atest InputTests:InputDeviceBatteryControllerTests */ @Presubmit class BatteryControllerTests { @@ -198,13 +198,14 @@ class BatteryControllerTests { private lateinit var context: TestableContext private lateinit var testLooper: TestLooper private lateinit var devicesChangedListener: IInputDevicesChangedListener + private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession private val deviceGenerationMap = mutableMapOf<Int /*deviceId*/, Int /*generation*/>() @Before fun setup() { context = TestableContext(ApplicationProvider.getApplicationContext()) testLooper = TestLooper() - InputManagerGlobal.resetInstance(iInputManager) + inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager) val inputManager = InputManager(context) context.addMockSystemService(InputManager::class.java, inputManager) `when`(iInputManager.inputDeviceIds).then { @@ -222,6 +223,13 @@ class BatteryControllerTests { testLooper.dispatchAll() } + @After + fun tearDown() { + if (this::inputManagerGlobalSession.isInitialized) { + inputManagerGlobalSession.close() + } + } + private fun notifyDeviceChanged( deviceId: Int, hasBattery: Boolean = true, @@ -253,11 +261,6 @@ class BatteryControllerTests { .adapter.getRemoteDevice(address) } - @After - fun tearDown() { - InputManagerGlobal.clearInstance() - } - @Test fun testRegisterAndUnregisterBinderLifecycle() { val listener = createMockListener() diff --git a/services/tests/servicestests/src/com/android/server/input/ConfigurationProcessorTest.java b/tests/Input/src/com/android/server/input/ConfigurationProcessorTest.java index 2bd4a3a397b1..9a49d91b1019 100644 --- a/services/tests/servicestests/src/com/android/server/input/ConfigurationProcessorTest.java +++ b/tests/Input/src/com/android/server/input/ConfigurationProcessorTest.java @@ -22,7 +22,7 @@ import static org.junit.Assert.fail; import android.content.Context; -import androidx.test.InstrumentationRegistry; +import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import org.junit.Before; @@ -43,12 +43,12 @@ public class ConfigurationProcessorTest { @Before public void setUp() throws Exception { - mContext = InstrumentationRegistry.getContext(); + mContext = InstrumentationRegistry.getInstrumentation().getContext(); } @Test public void testGetInputPortAssociations() { - final int res = com.android.frameworks.servicestests.R.raw.input_port_associations; + final int res = com.android.test.input.R.raw.input_port_associations; InputStream xml = mContext.getResources().openRawResource(res); Map<String, Integer> associations = null; try { @@ -65,7 +65,7 @@ public class ConfigurationProcessorTest { @Test public void testGetInputPortAssociationsBadDisplayport() { final int res = - com.android.frameworks.servicestests.R.raw.input_port_associations_bad_displayport; + com.android.test.input.R.raw.input_port_associations_bad_displayport; InputStream xml = mContext.getResources().openRawResource(res); Map<String, Integer> associations = null; try { @@ -79,7 +79,7 @@ public class ConfigurationProcessorTest { @Test public void testGetInputPortAssociationsEmptyConfig() { - final int res = com.android.frameworks.servicestests.R.raw.input_port_associations_bad_xml; + final int res = com.android.test.input.R.raw.input_port_associations_bad_xml; InputStream xml = mContext.getResources().openRawResource(res); try { ConfigurationProcessor.processInputPortAssociations(xml); diff --git a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt index 498776db3ac8..93a558287bd6 100644 --- a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt +++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt @@ -20,6 +20,8 @@ package com.android.server.input import android.content.Context import android.content.ContextWrapper import android.hardware.display.DisplayViewport +import android.hardware.input.InputManager +import android.hardware.input.InputManagerGlobal import android.os.IInputConstants import android.os.test.TestLooper import android.platform.test.annotations.Presubmit @@ -27,9 +29,10 @@ import android.provider.Settings import android.test.mock.MockContentResolver import android.view.Display import android.view.PointerIcon -import androidx.test.InstrumentationRegistry +import androidx.test.platform.app.InstrumentationRegistry import com.android.internal.util.test.FakeSettingsProvider import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before @@ -59,7 +62,7 @@ import java.util.concurrent.TimeUnit * Tests for {@link InputManagerService}. * * Build/Install/Run: - * atest FrameworksServicesTests:InputManagerServiceTests + * atest InputTests:InputManagerServiceTests */ @Presubmit class InputManagerServiceTests { @@ -84,10 +87,11 @@ class InputManagerServiceTests { private lateinit var context: Context private lateinit var testLooper: TestLooper private lateinit var contentResolver: MockContentResolver + private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession @Before fun setup() { - context = spy(ContextWrapper(InstrumentationRegistry.getContext())) + context = spy(ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext())) contentResolver = MockContentResolver(context) contentResolver.addProvider(Settings.AUTHORITY, FakeSettingsProvider()) whenever(context.contentResolver).thenReturn(contentResolver) @@ -105,10 +109,22 @@ class InputManagerServiceTests { localService = service!! } }) + inputManagerGlobalSession = InputManagerGlobal.createTestSession(service) + val inputManager = InputManager(context) + whenever(context.getSystemService(InputManager::class.java)).thenReturn(inputManager) + whenever(context.getSystemService(Context.INPUT_SERVICE)).thenReturn(inputManager) + assertTrue("Local service must be registered", this::localService.isInitialized) service.setWindowManagerCallbacks(wmCallbacks) } + @After + fun tearDown() { + if (this::inputManagerGlobalSession.isInitialized) { + inputManagerGlobalSession.close() + } + } + @Test fun testStart() { verifyZeroInteractions(native) diff --git a/services/tests/servicestests/src/com/android/server/input/KeyRemapperTests.kt b/tests/Input/src/com/android/server/input/KeyRemapperTests.kt index c10f65195307..f74fd723d540 100644 --- a/services/tests/servicestests/src/com/android/server/input/KeyRemapperTests.kt +++ b/tests/Input/src/com/android/server/input/KeyRemapperTests.kt @@ -54,7 +54,7 @@ private fun createKeyboard(deviceId: Int): InputDevice = * Tests for {@link KeyRemapper}. * * Build/Install/Run: - * atest FrameworksServicesTests:KeyRemapperTests + * atest InputTests:KeyRemapperTests */ @Presubmit class KeyRemapperTests { @@ -81,6 +81,7 @@ class KeyRemapperTests { private lateinit var context: Context private lateinit var dataStore: PersistentDataStore private lateinit var testLooper: TestLooper + private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession @Before fun setup() { @@ -103,7 +104,7 @@ class KeyRemapperTests { dataStore, testLooper.looper ) - InputManagerGlobal.resetInstance(iInputManager) + inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager) val inputManager = InputManager(context) Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))) .thenReturn(inputManager) @@ -112,7 +113,9 @@ class KeyRemapperTests { @After fun tearDown() { - InputManagerGlobal.clearInstance() + if (this::inputManagerGlobalSession.isInitialized) { + inputManagerGlobalSession.close() + } } @Test diff --git a/services/tests/servicestests/src/com/android/server/input/KeyboardBacklightControllerTests.kt b/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt index 3f4a4fba841e..59aa96c46336 100644 --- a/services/tests/servicestests/src/com/android/server/input/KeyboardBacklightControllerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt @@ -87,7 +87,7 @@ private fun createLight(lightId: Int, lightType: Int, suggestedBrightnessLevels: * Tests for {@link KeyboardBacklightController}. * * Build/Install/Run: - * atest FrameworksServicesTests:KeyboardBacklightControllerTests + * atest InputTests:KeyboardBacklightControllerTests */ @Presubmit class KeyboardBacklightControllerTests { @@ -111,6 +111,7 @@ class KeyboardBacklightControllerTests { private lateinit var context: Context private lateinit var dataStore: PersistentDataStore private lateinit var testLooper: TestLooper + private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession private var lightColorMap: HashMap<Int, Int> = HashMap() private var lastBacklightState: KeyboardBacklightState? = null private var sysfsNodeChanges = 0 @@ -133,7 +134,7 @@ class KeyboardBacklightControllerTests { testLooper = TestLooper() keyboardBacklightController = KeyboardBacklightController(context, native, dataStore, testLooper.looper, FakeAnimatorFactory(), uEventManager) - InputManagerGlobal.resetInstance(iInputManager) + inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager) val inputManager = InputManager(context) `when`(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager) `when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID)) @@ -153,7 +154,9 @@ class KeyboardBacklightControllerTests { @After fun tearDown() { - InputManagerGlobal.clearInstance() + if (this::inputManagerGlobalSession.isInitialized) { + inputManagerGlobalSession.close() + } } @Test diff --git a/services/tests/servicestests/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt index 55c45df2b844..b64775103ab2 100644 --- a/services/tests/servicestests/src/com/android/server/input/KeyboardLayoutManagerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt @@ -37,6 +37,7 @@ import android.view.inputmethod.InputMethodInfo import android.view.inputmethod.InputMethodSubtype import androidx.test.core.R import androidx.test.core.app.ApplicationProvider +import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertNotEquals import org.junit.Assert.assertNull @@ -77,7 +78,7 @@ private fun createKeyboard( * Tests for {@link Default UI} and {@link New UI}. * * Build/Install/Run: - * atest FrameworksServicesTests:KeyboardLayoutManagerTests + * atest InputTests:KeyboardLayoutManagerTests */ @Presubmit class KeyboardLayoutManagerTests { @@ -120,6 +121,7 @@ class KeyboardLayoutManagerTests { private lateinit var context: Context private lateinit var dataStore: PersistentDataStore private lateinit var testLooper: TestLooper + private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession // Devices private lateinit var keyboardDevice: InputDevice @@ -130,6 +132,7 @@ class KeyboardLayoutManagerTests { @Before fun setup() { context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext())) + inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager) dataStore = PersistentDataStore(object : PersistentDataStore.Injector() { override fun openRead(): InputStream? { throw FileNotFoundException() @@ -148,8 +151,14 @@ class KeyboardLayoutManagerTests { setupIme() } + @After + fun tearDown() { + if (this::inputManagerGlobalSession.isInitialized) { + inputManagerGlobalSession.close() + } + } + private fun setupInputDevices() { - InputManagerGlobal.resetInstance(iInputManager) val inputManager = InputManager(context) Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))) .thenReturn(inputManager) diff --git a/services/tests/servicestests/src/com/android/server/input/KeyboardMetricsCollectorTests.kt b/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt index c9724a3b4309..e2dc131a1d59 100644 --- a/services/tests/servicestests/src/com/android/server/input/KeyboardMetricsCollectorTests.kt +++ b/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt @@ -58,7 +58,7 @@ private fun createImeSubtype( * Tests for {@link KeyboardMetricsCollector}. * * Build/Install/Run: - * atest FrameworksServicesTests:KeyboardMetricsCollectorTests + * atest InputTests:KeyboardMetricsCollectorTests */ @Presubmit class KeyboardMetricsCollectorTests { |