diff options
78 files changed, 1302 insertions, 417 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index ec1e665dbdb1..78711086e2c8 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -189,6 +189,7 @@ package android { field public static final String START_VIEW_APP_FEATURES = "android.permission.START_VIEW_APP_FEATURES"; field public static final String START_VIEW_PERMISSION_USAGE = "android.permission.START_VIEW_PERMISSION_USAGE"; field public static final String STATUS_BAR = "android.permission.STATUS_BAR"; + field public static final String SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE = "android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE"; field public static final String SYSTEM_ALERT_WINDOW = "android.permission.SYSTEM_ALERT_WINDOW"; field public static final String TRANSMIT_IR = "android.permission.TRANSMIT_IR"; field public static final String UNINSTALL_SHORTCUT = "com.android.launcher.permission.UNINSTALL_SHORTCUT"; @@ -5559,11 +5560,11 @@ package android.app { public final class GameState implements android.os.Parcelable { ctor public GameState(boolean, int); - ctor public GameState(boolean, int, @Nullable String, @NonNull android.os.Bundle); + ctor public GameState(boolean, int, int, int); method public int describeContents(); - method @Nullable public String getDescription(); - method @NonNull public android.os.Bundle getMetadata(); + method public int getLabel(); method public int getMode(); + method public int getQuality(); method public boolean isLoading(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.GameState> CREATOR; @@ -5677,6 +5678,7 @@ package android.app { } public class KeyguardManager { + method @RequiresPermission(android.Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) public void addKeyguardLockedStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.KeyguardManager.KeyguardLockedStateListener); method @Deprecated public android.content.Intent createConfirmDeviceCredentialIntent(CharSequence, CharSequence); method @Deprecated @RequiresPermission(android.Manifest.permission.DISABLE_KEYGUARD) public void exitKeyguardSecurely(android.app.KeyguardManager.OnKeyguardExitResult); method @Deprecated public boolean inKeyguardRestrictedInputMode(); @@ -5685,6 +5687,7 @@ package android.app { method public boolean isKeyguardLocked(); method public boolean isKeyguardSecure(); method @Deprecated public android.app.KeyguardManager.KeyguardLock newKeyguardLock(String); + method @RequiresPermission(android.Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) public void removeKeyguardLockedStateListener(@NonNull android.app.KeyguardManager.KeyguardLockedStateListener); method public void requestDismissKeyguard(@NonNull android.app.Activity, @Nullable android.app.KeyguardManager.KeyguardDismissCallback); } @@ -5700,6 +5703,10 @@ package android.app { method @Deprecated @RequiresPermission(android.Manifest.permission.DISABLE_KEYGUARD) public void reenableKeyguard(); } + @java.lang.FunctionalInterface public static interface KeyguardManager.KeyguardLockedStateListener { + method public void onKeyguardLockedStateChanged(boolean); + } + @Deprecated public static interface KeyguardManager.OnKeyguardExitResult { method @Deprecated public void onKeyguardExitResult(boolean); } @@ -49292,7 +49299,7 @@ package android.view { method @NonNull public android.view.SurfaceControl.Transaction setDataSpace(@NonNull android.view.SurfaceControl, int); method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float, int); method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float, int, int); - method @NonNull public android.view.SurfaceControl.Transaction setGeometry(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect, int); + method @Deprecated @NonNull public android.view.SurfaceControl.Transaction setGeometry(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect, int); method @NonNull public android.view.SurfaceControl.Transaction setLayer(@NonNull android.view.SurfaceControl, @IntRange(from=java.lang.Integer.MIN_VALUE, to=java.lang.Integer.MAX_VALUE) int); method @NonNull public android.view.SurfaceControl.Transaction setOpaque(@NonNull android.view.SurfaceControl, boolean); method @NonNull public android.view.SurfaceControl.Transaction setPosition(@NonNull android.view.SurfaceControl, float, float); @@ -53112,7 +53119,7 @@ package android.view.inputmethod { field public static final int RESULT_SHOWN = 2; // 0x2 field public static final int RESULT_UNCHANGED_HIDDEN = 1; // 0x1 field public static final int RESULT_UNCHANGED_SHOWN = 0; // 0x0 - field public static final int SHOW_FORCED = 2; // 0x2 + field @Deprecated public static final int SHOW_FORCED = 2; // 0x2 field public static final int SHOW_IMPLICIT = 1; // 0x1 } diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 9757b2ff318f..e64392b3b0c2 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2347,6 +2347,8 @@ package android.service.dreams { public abstract class DreamOverlayService extends android.app.Service { ctor public DreamOverlayService(); + method @Nullable public final CharSequence getDreamLabel(); + method public final boolean isPreviewMode(); method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); method public abstract void onStartDream(@NonNull android.view.WindowManager.LayoutParams); method public final void requestExit(); @@ -3082,6 +3084,7 @@ package android.view.inputmethod { method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodListAsUser(int); method public boolean hasActiveInputConnection(@Nullable android.view.View); method public boolean isInputMethodPickerShown(); + field public static final long CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING = 214016041L; // 0xcc1a029L } } diff --git a/core/java/android/app/GameState.java b/core/java/android/app/GameState.java index 979dd34e8276..fe6e5543a662 100644 --- a/core/java/android/app/GameState.java +++ b/core/java/android/app/GameState.java @@ -18,8 +18,6 @@ package android.app; import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -88,12 +86,11 @@ public final class GameState implements Parcelable { // One of the states listed above. private final @GameStateMode int mMode; - // This is a game specific description. For example can be level or scene name. - private final @Nullable String mDescription; + // A developer-supplied enum, e.g. to indicate level or scene. + private final int mLabel; - // This contains any other game specific parameters not covered by the fields above. It can be - // quality parameter data, settings, or game modes. - private final @NonNull Bundle mMetaData; + // The developer-supplied enum, e.g. to indicate the current quality level. + private final int mQuality; /** * Create a GameState with the specified loading status. @@ -101,29 +98,28 @@ public final class GameState implements Parcelable { * @param mode The game state mode of type @GameStateMode. */ public GameState(boolean isLoading, @GameStateMode int mode) { - this(isLoading, mode, null, new Bundle()); + this(isLoading, mode, -1, -1); } /** * Create a GameState with the given state variables. * @param isLoading Whether the game is in the loading state. - * @param mode The game state mode of type @GameStateMode. - * @param description An optional description of the state. - * @param metaData Optional metadata. + * @param mode The game state mode. + * @param label An optional developer-supplied enum e.g. for the current level. + * @param quality An optional developer-supplied enum, e.g. for the current quality level. */ - public GameState(boolean isLoading, @GameStateMode int mode, @Nullable String description, - @NonNull Bundle metaData) { + public GameState(boolean isLoading, @GameStateMode int mode, int label, int quality) { mIsLoading = isLoading; mMode = mode; - mDescription = description; - mMetaData = metaData; + mLabel = label; + mQuality = quality; } private GameState(Parcel in) { mIsLoading = in.readBoolean(); mMode = in.readInt(); - mDescription = in.readString(); - mMetaData = in.readBundle(); + mLabel = in.readInt(); + mQuality = in.readInt(); } /** @@ -141,17 +137,19 @@ public final class GameState implements Parcelable { } /** - * @return The state description, or null if one is not set. + * @return The developer-supplied enum, e.g. to indicate level or scene. The default value (if + * not supplied) is -1. */ - public @Nullable String getDescription() { - return mDescription; + public int getLabel() { + return mLabel; } /** - * @return metadata associated with the state. + * @return The developer-supplied enum, e.g. to indicate the current quality level. The default + * value (if not suplied) is -1. */ - public @NonNull Bundle getMetadata() { - return mMetaData; + public int getQuality() { + return mQuality; } @Override @@ -163,8 +161,8 @@ public final class GameState implements Parcelable { public void writeToParcel(@NonNull Parcel parcel, int flags) { parcel.writeBoolean(mIsLoading); parcel.writeInt(mMode); - parcel.writeString(mDescription); - parcel.writeBundle(mMetaData); + parcel.writeInt(mLabel); + parcel.writeInt(mQuality); } /** diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index 99100003e5b3..87ba197d8052 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -52,6 +52,7 @@ import android.view.WindowManager.LayoutParams; import android.view.WindowManagerGlobal; import com.android.internal.policy.IKeyguardDismissCallback; +import com.android.internal.policy.IKeyguardLockedStateListener; import com.android.internal.util.Preconditions; import com.android.internal.widget.IWeakEscrowTokenActivatedListener; import com.android.internal.widget.IWeakEscrowTokenRemovedListener; @@ -183,6 +184,10 @@ public class KeyguardManager { }) @interface LockTypes {} + // TODO(b/220379118): register only one binder listener and keep a map of listener to executor. + private final ArrayMap<KeyguardLockedStateListener, IKeyguardLockedStateListener> + mKeyguardLockedStateListeners = new ArrayMap<>(); + /** * Get an intent to prompt the user to confirm credentials (pin, pattern, password or biometrics * if enrolled) for the current user of the device. The caller is expected to launch this @@ -534,7 +539,7 @@ public class KeyguardManager { /** * Return whether the keyguard is currently locked. * - * @return true if keyguard is locked. + * @return {@code true} if the keyguard is locked. */ public boolean isKeyguardLocked() { try { @@ -550,7 +555,7 @@ public class KeyguardManager { * * <p>See also {@link #isDeviceSecure()} which ignores SIM locked states. * - * @return true if a PIN, pattern or password is set or a SIM card is locked. + * @return {@code true} if a PIN, pattern or password is set or a SIM card is locked. */ public boolean isKeyguardSecure() { try { @@ -565,7 +570,7 @@ public class KeyguardManager { * keyguard password emergency screen). When in such mode, certain keys, * such as the Home key and the right soft keys, don't work. * - * @return true if in keyguard restricted input mode. + * @return {@code true} if in keyguard restricted input mode. * @deprecated Use {@link #isKeyguardLocked()} instead. */ public boolean inKeyguardRestrictedInputMode() { @@ -576,7 +581,7 @@ public class KeyguardManager { * Returns whether the device is currently locked and requires a PIN, pattern or * password to unlock. * - * @return true if unlocking the device currently requires a PIN, pattern or + * @return {@code true} if unlocking the device currently requires a PIN, pattern or * password. */ public boolean isDeviceLocked() { @@ -603,7 +608,7 @@ public class KeyguardManager { * * <p>See also {@link #isKeyguardSecure} which treats SIM locked states as secure. * - * @return true if a PIN, pattern or password was set. + * @return {@code true} if a PIN, pattern or password was set. */ public boolean isDeviceSecure() { return isDeviceSecure(mContext.getUserId()); @@ -762,7 +767,7 @@ public class KeyguardManager { * as the output of String#getBytes * @param complexity - complexity level imposed by the requester * as defined in {@code DevicePolicyManager.PasswordComplexity} - * @return true if the password is valid, false otherwise + * @return {@code true} if the password is valid, false otherwise * @hide */ @RequiresPermission(Manifest.permission.SET_INITIAL_LOCK) @@ -821,7 +826,7 @@ public class KeyguardManager { * as the output of String#getBytes * @param complexity - complexity level imposed by the requester * as defined in {@code DevicePolicyManager.PasswordComplexity} - * @return true if the lock is successfully set, false otherwise + * @return {@code true} if the lock is successfully set, false otherwise * @hide */ @RequiresPermission(Manifest.permission.SET_INITIAL_LOCK) @@ -903,8 +908,8 @@ public class KeyguardManager { /** * Remove a weak escrow token. * - * @return true if the given handle refers to a valid weak token previously returned from - * {@link #addWeakEscrowToken}, whether it's active or not. return false otherwise. + * @return {@code true} if the given handle refers to a valid weak token previously returned + * from {@link #addWeakEscrowToken}, whether it's active or not. return false otherwise. * @hide */ @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE) @@ -944,7 +949,7 @@ public class KeyguardManager { /** * Register the given WeakEscrowTokenRemovedListener. * - * @return true if the listener is registered successfully, return false otherwise. + * @return {@code true} if the listener is registered successfully, return false otherwise. * @hide */ @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE) @@ -982,7 +987,7 @@ public class KeyguardManager { /** * Unregister the given WeakEscrowTokenRemovedListener. * - * @return true if the listener is unregistered successfully, return false otherwise. + * @return {@code true} if the listener is unregistered successfully, return false otherwise. * @hide */ @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE) @@ -1076,4 +1081,61 @@ public class KeyguardManager { throw new IllegalArgumentException("Unknown lock type " + lockType); } } + + /** + * Listener for keyguard locked state changes. + */ + @FunctionalInterface + public interface KeyguardLockedStateListener { + /** + * Callback function that executes when the keyguard locked state changes. + */ + void onKeyguardLockedStateChanged(boolean isKeyguardLocked); + } + + /** + * Registers a listener to execute when the keyguard visibility changes. + * + * @param listener The listener to add to receive keyguard visibility changes. + */ + @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) + public void addKeyguardLockedStateListener(@NonNull @CallbackExecutor Executor executor, + @NonNull KeyguardLockedStateListener listener) { + synchronized (mKeyguardLockedStateListeners) { + try { + final IKeyguardLockedStateListener innerListener = + new IKeyguardLockedStateListener.Stub() { + @Override + public void onKeyguardLockedStateChanged(boolean isKeyguardLocked) { + executor.execute( + () -> listener.onKeyguardLockedStateChanged(isKeyguardLocked)); + } + }; + mWM.addKeyguardLockedStateListener(innerListener); + mKeyguardLockedStateListeners.put(listener, innerListener); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Unregisters a listener that executes when the keyguard visibility changes. + */ + @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) + public void removeKeyguardLockedStateListener(@NonNull KeyguardLockedStateListener listener) { + synchronized (mKeyguardLockedStateListeners) { + IKeyguardLockedStateListener innerListener = mKeyguardLockedStateListeners.get( + listener); + if (innerListener == null) { + return; + } + try { + mWM.removeKeyguardLockedStateListener(innerListener); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + mKeyguardLockedStateListeners.remove(listener); + } + } } diff --git a/core/java/android/service/dreams/DreamOverlayService.java b/core/java/android/service/dreams/DreamOverlayService.java index 163d6ed4b18b..bfc3b8b39385 100644 --- a/core/java/android/service/dreams/DreamOverlayService.java +++ b/core/java/android/service/dreams/DreamOverlayService.java @@ -36,6 +36,9 @@ public abstract class DreamOverlayService extends Service { private static final String TAG = "DreamOverlayService"; private static final boolean DEBUG = false; private boolean mShowComplications; + private boolean mIsPreviewMode; + @Nullable + private CharSequence mDreamLabel; private IDreamOverlay mDreamOverlay = new IDreamOverlay.Stub() { @Override @@ -56,6 +59,8 @@ public abstract class DreamOverlayService extends Service { public final IBinder onBind(@NonNull Intent intent) { mShowComplications = intent.getBooleanExtra(DreamService.EXTRA_SHOW_COMPLICATIONS, DreamService.DEFAULT_SHOW_COMPLICATIONS); + mIsPreviewMode = intent.getBooleanExtra(DreamService.EXTRA_IS_PREVIEW, false); + mDreamLabel = intent.getCharSequenceExtra(DreamService.EXTRA_DREAM_LABEL); return mDreamOverlay.asBinder(); } @@ -84,4 +89,19 @@ public abstract class DreamOverlayService extends Service { public final boolean shouldShowComplications() { return mShowComplications; } + + /** + * Returns whether the dream is running in preview mode. + */ + public final boolean isPreviewMode() { + return mIsPreviewMode; + } + + /** + * Returns the user-facing label of the currently running dream. + */ + @Nullable + public final CharSequence getDreamLabel() { + return mDreamLabel; + } } diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 345917220b6b..db622d39b785 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -216,6 +216,18 @@ public class DreamService extends Service implements Window.Callback { "android.service.dreams.SHOW_COMPLICATIONS"; /** + * Extra containing a boolean for whether we are showing this dream in preview mode. + * @hide + */ + public static final String EXTRA_IS_PREVIEW = "android.service.dreams.IS_PREVIEW"; + + /** + * The user-facing label of the current dream service. + * @hide + */ + public static final String EXTRA_DREAM_LABEL = "android.service.dreams.DREAM_LABEL"; + + /** * The default value for whether to show complications on the overlay. * @hide */ @@ -258,15 +270,19 @@ public class DreamService extends Service implements Window.Callback { } public void bind(Context context, @Nullable ComponentName overlayService, - ComponentName dreamService) { + ComponentName dreamService, boolean isPreviewMode) { if (overlayService == null) { return; } + final ServiceInfo serviceInfo = fetchServiceInfo(context, dreamService); + final Intent overlayIntent = new Intent(); overlayIntent.setComponent(overlayService); overlayIntent.putExtra(EXTRA_SHOW_COMPLICATIONS, - fetchShouldShowComplications(context, dreamService)); + fetchShouldShowComplications(context, serviceInfo)); + overlayIntent.putExtra(EXTRA_DREAM_LABEL, fetchDreamLabel(context, serviceInfo)); + overlayIntent.putExtra(EXTRA_IS_PREVIEW, isPreviewMode); context.bindService(overlayIntent, this, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE); @@ -988,8 +1004,11 @@ public class DreamService extends Service implements Window.Callback { // Connect to the overlay service if present. if (!mWindowless) { - mOverlayConnection.bind(this, intent.getParcelableExtra(EXTRA_DREAM_OVERLAY_COMPONENT), - new ComponentName(this, getClass())); + mOverlayConnection.bind( + /* context= */ this, + intent.getParcelableExtra(EXTRA_DREAM_OVERLAY_COMPONENT), + new ComponentName(this, getClass()), + intent.getBooleanExtra(EXTRA_IS_PREVIEW, /* defaultValue= */ false)); } return mDreamServiceWrapper; @@ -1111,7 +1130,10 @@ public class DreamService extends Service implements Window.Callback { * @hide */ @Nullable - public static DreamMetadata getDreamMetadata(Context context, ServiceInfo serviceInfo) { + public static DreamMetadata getDreamMetadata(Context context, + @Nullable ServiceInfo serviceInfo) { + if (serviceInfo == null) return null; + final PackageManager pm = context.getPackageManager(); final TypedArray rawMetadata = readMetadata(pm, serviceInfo); @@ -1350,22 +1372,33 @@ public class DreamService extends Service implements Window.Callback { * {@link DreamService#DEFAULT_SHOW_COMPLICATIONS}. */ private static boolean fetchShouldShowComplications(Context context, - ComponentName componentName) { + @Nullable ServiceInfo serviceInfo) { + final DreamMetadata metadata = getDreamMetadata(context, serviceInfo); + if (metadata != null) { + return metadata.showComplications; + } + return DEFAULT_SHOW_COMPLICATIONS; + } + + @Nullable + private static CharSequence fetchDreamLabel(Context context, + @Nullable ServiceInfo serviceInfo) { + if (serviceInfo == null) return null; + final PackageManager pm = context.getPackageManager(); + return serviceInfo.loadLabel(pm); + } + + @Nullable + private static ServiceInfo fetchServiceInfo(Context context, ComponentName componentName) { final PackageManager pm = context.getPackageManager(); try { - final ServiceInfo si = pm.getServiceInfo(componentName, + return pm.getServiceInfo(componentName, PackageManager.ComponentInfoFlags.of(PackageManager.GET_META_DATA)); - final DreamMetadata metadata = getDreamMetadata(context, si); - - if (metadata != null) { - return metadata.showComplications; - } } catch (PackageManager.NameNotFoundException e) { if (DEBUG) Log.w(TAG, "cannot find component " + componentName.flattenToShortString()); } - - return DEFAULT_SHOW_COMPLICATIONS; + return null; } @Override diff --git a/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java b/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java index 8fe6f71b598d..adc9251d89be 100644 --- a/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java +++ b/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java @@ -40,7 +40,7 @@ public class FloatingToolbarRoot extends LinearLayout { private final IBinder mTargetInputToken; private final SelectionToolbarRenderService.TransferTouchListener mTransferTouchListener; - private Rect mContentRect; + private final Rect mContentRect = new Rect(); private int mLastDownX = -1; private int mLastDownY = -1; @@ -57,7 +57,7 @@ public class FloatingToolbarRoot extends LinearLayout { * Sets the Rect that shows the selection toolbar content. */ public void setContentRect(Rect contentRect) { - mContentRect = contentRect; + mContentRect.set(contentRect); } @Override diff --git a/core/java/android/util/DumpableContainer.java b/core/java/android/util/DumpableContainer.java index e7b2acd5b500..fef5acd42e4e 100644 --- a/core/java/android/util/DumpableContainer.java +++ b/core/java/android/util/DumpableContainer.java @@ -18,8 +18,7 @@ package android.util; import android.annotation.NonNull; /** - * Objects that contains a list of {@link Dumpable}, which will be dumped when the object itself - * is dumped. + * Represents a container that manages {@link Dumpable dumpables}. */ public interface DumpableContainer { diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index ce21086931da..53b842a0f3a2 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -18,6 +18,7 @@ package android.view; import com.android.internal.os.IResultReceiver; import com.android.internal.policy.IKeyguardDismissCallback; +import com.android.internal.policy.IKeyguardLockedStateListener; import com.android.internal.policy.IShortcutService; import android.app.IAssistDataReceiver; @@ -199,6 +200,9 @@ interface IWindowManager boolean isKeyguardSecure(int userId); void dismissKeyguard(IKeyguardDismissCallback callback, CharSequence message); + void addKeyguardLockedStateListener(in IKeyguardLockedStateListener listener); + void removeKeyguardLockedStateListener(in IKeyguardLockedStateListener listener); + // Requires INTERACT_ACROSS_USERS_FULL permission void setSwitchingUser(boolean switching); diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 98cef95885bd..632af2315bcd 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -3094,6 +3094,10 @@ public final class SurfaceControl implements Parcelable { * @param destFrame The destination rectangle in parent space. Or null for the source frame. * @param orientation The buffer rotation * @return This transaction object. + * @deprecated Use {@link #setCrop(SurfaceControl, Rect)}, + * {@link #setBufferTransform(SurfaceControl, int)}, + * {@link #setPosition(SurfaceControl, float, float)} and + * {@link #setScale(SurfaceControl, float, float)} instead. */ @NonNull public Transaction setGeometry(@NonNull SurfaceControl sc, @Nullable Rect sourceCrop, diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index 2edfda5d065c..a13579d0acad 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -328,7 +328,8 @@ public class SurfaceControlViewHost { */ public @Nullable SurfacePackage getSurfacePackage() { if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) { - return new SurfacePackage(mSurfaceControl, mAccessibilityEmbeddedConnection, + return new SurfacePackage(new SurfaceControl(mSurfaceControl, "getSurfacePackage"), + mAccessibilityEmbeddedConnection, mWm.getFocusGrantToken(), mRemoteInterface); } else { return null; diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 27174630bfa3..017b8aa3cf6c 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -46,6 +46,8 @@ import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.UserIdInt; import android.app.ActivityThread; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.ContentResolver; @@ -356,6 +358,21 @@ public final class InputMethodManager { /** @hide */ public static final int SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES = 2; + /** + * Clear {@link #SHOW_FORCED} flag when the next IME focused application changed. + * + * <p> + * Note that when this flag enabled in server side, {@link #SHOW_FORCED} will no longer + * affect the next focused application to keep showing IME, in case of unexpected IME visible + * when the next focused app isn't be the IME requester. </p> + * + * @hide + */ + @TestApi + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) + public static final long CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING = 214016041L; // This is a bug id. + @UnsupportedAppUsage final IInputMethodManager mService; final Looper mMainLooper; @@ -1646,7 +1663,14 @@ public final class InputMethodManager { * Flag for {@link #showSoftInput} to indicate that the user has forced * the input method open (such as by long-pressing menu) so it should * not be closed until they explicitly do so. + * + * @deprecated Use {@link #showSoftInput} without this flag instead. Using this flag can lead + * to the soft input remaining visible even when the calling application is closed. The + * use of this flag can make the soft input remains visible globally. Starting in + * {@link Build.VERSION_CODES#TIRAMISU Android T}, this flag only has an effect while the + * caller is currently focused. */ + @Deprecated public static final int SHOW_FORCED = 0x0002; /** diff --git a/core/java/com/android/internal/policy/IKeyguardLockedStateListener.aidl b/core/java/com/android/internal/policy/IKeyguardLockedStateListener.aidl new file mode 100644 index 000000000000..ee5021918879 --- /dev/null +++ b/core/java/com/android/internal/policy/IKeyguardLockedStateListener.aidl @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.policy; + +oneway interface IKeyguardLockedStateListener { + void onKeyguardLockedStateChanged(boolean isKeyguardLocked); +}
\ No newline at end of file diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index d4c03e412fcb..58a3bb4818df 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4119,6 +4119,13 @@ <permission android:name="android.permission.MANAGE_HOTWORD_DETECTION" android:protectionLevel="internal|preinstalled" /> + <!-- Allows an application to subscribe to keyguard locked (i.e., showing) state. + <p>Protection level: internal|role + <p>Intended for use by ROLE_ASSISTANT only. + --> + <permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" + android:protectionLevel="internal|role"/> + <!-- Must be required by a {@link android.service.autofill.AutofillService}, to ensure that only the system can bind to it. <p>Protection level: signature @@ -6912,6 +6919,10 @@ android:permission="android.permission.BIND_JOB_SERVICE"> </service> + <service android:name="com.android.server.appsearch.contactsindexer.ContactsIndexerMaintenanceService" + android:permission="android.permission.BIND_JOB_SERVICE"> + </service> + <service android:name="com.android.server.pm.PackageManagerShellCommandDataLoader" android:exported="false"> <intent-filter> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 744c3dab9510..adf8f8e99d35 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -606,6 +606,9 @@ <!-- The padding ratio of the Accessibility icon foreground drawable --> <item name="accessibility_icon_foreground_padding_ratio" type="dimen">21.88%</item> + <!-- The minimum window size of the accessibility window magnifier --> + <dimen name="accessibility_window_magnifier_min_size">122dp</dimen> + <!-- Margin around the various security views --> <dimen name="keyguard_muliuser_selector_margin">8dp</dimen> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index d4513d0c4a3d..558e3c3a0222 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4399,6 +4399,7 @@ <java-symbol type="color" name="accessibility_focus_highlight_color" /> <!-- Width of the outline stroke used by the accessibility focus rectangle --> <java-symbol type="dimen" name="accessibility_focus_highlight_stroke_width" /> + <java-symbol type="dimen" name="accessibility_window_magnifier_min_size" /> <java-symbol type="bool" name="config_attachNavBarToAppDuringTransition" /> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index f7c92fed5522..8a482fbfa1c4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -144,21 +144,42 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange return Math.max(dividerInset, radius); } - /** Gets bounds of the primary split. */ + /** Gets bounds of the primary split with screen based coordinate. */ public Rect getBounds1() { return new Rect(mBounds1); } - /** Gets bounds of the secondary split. */ + /** Gets bounds of the primary split with parent based coordinate. */ + public Rect getRefBounds1() { + Rect outBounds = getBounds1(); + outBounds.offset(-mRootBounds.left, -mRootBounds.top); + return outBounds; + } + + /** Gets bounds of the secondary split with screen based coordinate. */ public Rect getBounds2() { return new Rect(mBounds2); } - /** Gets bounds of divider window. */ + /** Gets bounds of the secondary split with parent based coordinate. */ + public Rect getRefBounds2() { + final Rect outBounds = getBounds2(); + outBounds.offset(-mRootBounds.left, -mRootBounds.top); + return outBounds; + } + + /** Gets bounds of divider window with screen based coordinate. */ public Rect getDividerBounds() { return new Rect(mDividerBounds); } + /** Gets bounds of divider window with parent based coordinate. */ + public Rect getRefDividerBounds() { + final Rect outBounds = getDividerBounds(); + outBounds.offset(-mRootBounds.left, -mRootBounds.top); + return outBounds; + } + /** Returns leash of the current divider bar. */ @Nullable public SurfaceControl getDividerLeash() { @@ -452,14 +473,17 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange SurfaceControl leash2, SurfaceControl dimLayer1, SurfaceControl dimLayer2) { final SurfaceControl dividerLeash = getDividerLeash(); if (dividerLeash != null) { - t.setPosition(dividerLeash, mDividerBounds.left, mDividerBounds.top); + mTempRect.set(getRefDividerBounds()); + t.setPosition(dividerLeash, mTempRect.left, mTempRect.top); // Resets layer of divider bar to make sure it is always on top. t.setLayer(dividerLeash, SPLIT_DIVIDER_LAYER); } - t.setPosition(leash1, mBounds1.left, mBounds1.top) - .setWindowCrop(leash1, mBounds1.width(), mBounds1.height()); - t.setPosition(leash2, mBounds2.left, mBounds2.top) - .setWindowCrop(leash2, mBounds2.width(), mBounds2.height()); + mTempRect.set(getRefBounds1()); + t.setPosition(leash1, mTempRect.left, mTempRect.top) + .setWindowCrop(leash1, mTempRect.width(), mTempRect.height()); + mTempRect.set(getRefBounds2()); + t.setPosition(leash2, mTempRect.left, mTempRect.top) + .setWindowCrop(leash2, mTempRect.width(), mTempRect.height()); if (mImePositionProcessor.adjustSurfaceLayoutForIme( t, dividerLeash, leash1, leash2, dimLayer1, dimLayer2)) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java index 180e3fb48c9d..d7322ce7beda 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java @@ -138,8 +138,8 @@ public class PipSurfaceTransactionHelper { // destination are different. final float scale = srcW <= srcH ? (float) destW / srcW : (float) destH / srcH; final Rect crop = mTmpDestinationRect; - crop.set(0, 0, Transitions.ENABLE_SHELL_TRANSITIONS ? destH - : destW, Transitions.ENABLE_SHELL_TRANSITIONS ? destW : destH); + crop.set(0, 0, Transitions.SHELL_TRANSITIONS_ROTATION ? destH + : destW, Transitions.SHELL_TRANSITIONS_ROTATION ? destW : destH); // Inverse scale for crop to fit in screen coordinates. crop.scale(1 / scale); crop.offset(insets.left, insets.top); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 60aac6806623..91615fe05417 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -97,7 +97,7 @@ public class PipTransition extends PipTransitionController { * meaningful if {@link #mInFixedRotation} is true. */ @Surface.Rotation - private int mFixedRotation; + private int mEndFixedRotation; /** Whether the PIP window has fade out for fixed rotation. */ private boolean mHasFadeOut; @@ -153,7 +153,7 @@ public class PipTransition extends PipTransitionController { final TransitionInfo.Change currentPipChange = findCurrentPipChange(info); final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info); mInFixedRotation = fixedRotationChange != null; - mFixedRotation = mInFixedRotation + mEndFixedRotation = mInFixedRotation ? fixedRotationChange.getEndFixedRotation() : ROTATION_UNDEFINED; @@ -257,7 +257,7 @@ public class PipTransition extends PipTransitionController { final ActivityManager.RunningTaskInfo taskInfo = mPipOrganizer.getTaskInfo(); if (taskInfo != null) { startExpandAnimation(taskInfo, mPipOrganizer.getSurfaceControl(), - new Rect(mExitDestinationBounds)); + new Rect(mExitDestinationBounds), Surface.ROTATION_0); } mExitDestinationBounds.setEmpty(); mCurrentPipTaskToken = null; @@ -332,30 +332,31 @@ public class PipTransition extends PipTransitionController { @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback, @NonNull TransitionInfo.Change pipChange) { - TransitionInfo.Change displayRotationChange = null; - for (int i = info.getChanges().size() - 1; i >= 0; --i) { - final TransitionInfo.Change change = info.getChanges().get(i); - if (change.getMode() == TRANSIT_CHANGE - && (change.getFlags() & FLAG_IS_DISPLAY) != 0 - && change.getStartRotation() != change.getEndRotation()) { - displayRotationChange = change; - break; - } - } - - if (displayRotationChange != null) { - // Exiting PIP to fullscreen with orientation change. - startExpandAndRotationAnimation(info, startTransaction, finishTransaction, - finishCallback, displayRotationChange, pipChange); - return; - } - - // When there is no rotation, we can simply expand the PIP window. mFinishCallback = (wct, wctCB) -> { mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo()); finishCallback.onTransitionFinished(wct, wctCB); }; + // Check if it is Shell rotation. + if (Transitions.SHELL_TRANSITIONS_ROTATION) { + TransitionInfo.Change displayRotationChange = null; + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + if (change.getMode() == TRANSIT_CHANGE + && (change.getFlags() & FLAG_IS_DISPLAY) != 0 + && change.getStartRotation() != change.getEndRotation()) { + displayRotationChange = change; + break; + } + } + if (displayRotationChange != null) { + // Exiting PIP to fullscreen with orientation change. + startExpandAndRotationAnimation(info, startTransaction, finishTransaction, + displayRotationChange, pipChange); + return; + } + } + // Set the initial frame as scaling the end to the start. final Rect destinationBounds = new Rect(pipChange.getEndAbsBounds()); final Point offset = pipChange.getEndRelOffset(); @@ -364,13 +365,41 @@ public class PipTransition extends PipTransitionController { mSurfaceTransactionHelper.scale(startTransaction, pipChange.getLeash(), destinationBounds, mPipBoundsState.getBounds()); startTransaction.apply(); - startExpandAnimation(pipChange.getTaskInfo(), pipChange.getLeash(), destinationBounds); + + // Check if it is fixed rotation. + final int rotationDelta; + if (mInFixedRotation) { + final int startRotation = pipChange.getStartRotation(); + final int endRotation = mEndFixedRotation; + rotationDelta = deltaRotation(startRotation, endRotation); + final Rect endBounds = new Rect(destinationBounds); + + // Set the end frame since the display won't rotate until fixed rotation is finished + // in the next display change transition. + rotateBounds(endBounds, destinationBounds, rotationDelta); + final int degree, x, y; + if (rotationDelta == ROTATION_90) { + degree = 90; + x = destinationBounds.right; + y = destinationBounds.top; + } else { + degree = -90; + x = destinationBounds.left; + y = destinationBounds.bottom; + } + mSurfaceTransactionHelper.rotateAndScaleWithCrop(finishTransaction, + pipChange.getLeash(), endBounds, endBounds, new Rect(), degree, x, y, + true /* isExpanding */, rotationDelta == ROTATION_270 /* clockwise */); + } else { + rotationDelta = Surface.ROTATION_0; + } + startExpandAnimation(pipChange.getTaskInfo(), pipChange.getLeash(), destinationBounds, + rotationDelta); } private void startExpandAndRotationAnimation(@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback, @NonNull TransitionInfo.Change displayRotationChange, @NonNull TransitionInfo.Change pipChange) { final int rotateDelta = deltaRotation(displayRotationChange.getStartRotation(), @@ -380,11 +409,6 @@ public class PipTransition extends PipTransitionController { final CounterRotatorHelper rotator = new CounterRotatorHelper(); rotator.handleClosingChanges(info, startTransaction, displayRotationChange); - mFinishCallback = (wct, wctCB) -> { - mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo()); - finishCallback.onTransitionFinished(wct, wctCB); - }; - // Get the start bounds in new orientation. final Rect startBounds = new Rect(pipChange.getStartAbsBounds()); rotateBounds(startBounds, displayRotationChange.getStartAbsBounds(), rotateDelta); @@ -425,12 +449,11 @@ public class PipTransition extends PipTransitionController { } private void startExpandAnimation(final TaskInfo taskInfo, final SurfaceControl leash, - final Rect destinationBounds) { - PipAnimationController.PipTransitionAnimator animator = + final Rect destinationBounds, final int rotationDelta) { + final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController.getAnimator(taskInfo, leash, mPipBoundsState.getBounds(), mPipBoundsState.getBounds(), destinationBounds, null, - TRANSITION_DIRECTION_LEAVE_PIP, 0 /* startingAngle */, Surface.ROTATION_0); - + TRANSITION_DIRECTION_LEAVE_PIP, 0 /* startingAngle */, rotationDelta); animator.setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(mEnterExitAnimationDuration) @@ -526,7 +549,7 @@ public class PipTransition extends PipTransitionController { mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP); mFinishCallback = finishCallback; - final int endRotation = mInFixedRotation ? mFixedRotation : enterPip.getEndRotation(); + final int endRotation = mInFixedRotation ? mEndFixedRotation : enterPip.getEndRotation(); return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(), startTransaction, finishTransaction, enterPip.getStartRotation(), endRotation); @@ -545,8 +568,8 @@ public class PipTransition extends PipTransitionController { taskInfo.pictureInPictureParams, currentBounds); if (rotationDelta != Surface.ROTATION_0 && mInFixedRotation) { // Need to get the bounds of new rotation in old rotation for fixed rotation, - sourceHintRect = computeRotatedBounds(rotationDelta, startRotation, endRotation, - taskInfo, TRANSITION_DIRECTION_TO_PIP, destinationBounds, sourceHintRect); + computeEnterPipRotatedBounds(rotationDelta, startRotation, endRotation, taskInfo, + destinationBounds, sourceHintRect); } PipAnimationController.PipTransitionAnimator animator; // Set corner radius for entering pip. @@ -583,8 +606,6 @@ public class PipTransition extends PipTransitionController { startTransaction.setMatrix(leash, tmpTransform, new float[9]); } if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { - // Reverse the rotation for Shell transition animation. - rotationDelta = deltaRotation(rotationDelta, 0); animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds, currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */, rotationDelta); @@ -617,33 +638,22 @@ public class PipTransition extends PipTransitionController { return true; } - /** Computes destination bounds in old rotation and returns source hint rect if available. */ - @Nullable - private Rect computeRotatedBounds(int rotationDelta, int startRotation, int endRotation, - TaskInfo taskInfo, int direction, Rect outDestinationBounds, - @Nullable Rect sourceHintRect) { - if (direction == TRANSITION_DIRECTION_TO_PIP) { - mPipBoundsState.getDisplayLayout().rotateTo(mContext.getResources(), endRotation); - final Rect displayBounds = mPipBoundsState.getDisplayBounds(); - outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds()); - // Transform the destination bounds to current display coordinates. - rotateBounds(outDestinationBounds, displayBounds, endRotation, startRotation); - // When entering PiP (from button navigation mode), adjust the source rect hint by - // display cutout if applicable. - if (sourceHintRect != null && taskInfo.displayCutoutInsets != null) { - if (rotationDelta == Surface.ROTATION_270) { - sourceHintRect.offset(taskInfo.displayCutoutInsets.left, - taskInfo.displayCutoutInsets.top); - } + /** Computes destination bounds in old rotation and updates source hint rect if available. */ + private void computeEnterPipRotatedBounds(int rotationDelta, int startRotation, int endRotation, + TaskInfo taskInfo, Rect outDestinationBounds, @Nullable Rect outSourceHintRect) { + mPipBoundsState.getDisplayLayout().rotateTo(mContext.getResources(), endRotation); + final Rect displayBounds = mPipBoundsState.getDisplayBounds(); + outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds()); + // Transform the destination bounds to current display coordinates. + rotateBounds(outDestinationBounds, displayBounds, endRotation, startRotation); + // When entering PiP (from button navigation mode), adjust the source rect hint by + // display cutout if applicable. + if (outSourceHintRect != null && taskInfo.displayCutoutInsets != null) { + if (rotationDelta == Surface.ROTATION_270) { + outSourceHintRect.offset(taskInfo.displayCutoutInsets.left, + taskInfo.displayCutoutInsets.top); } - } else if (direction == TRANSITION_DIRECTION_LEAVE_PIP) { - final Rect rotatedDestinationBounds = new Rect(outDestinationBounds); - rotateBounds(rotatedDestinationBounds, mPipBoundsState.getDisplayBounds(), - rotationDelta); - return PipBoundsAlgorithm.getValidSourceHintRect(taskInfo.pictureInPictureParams, - rotatedDestinationBounds); - } - return sourceHintRect; + } } private void startExitToSplitAnimation(TransitionInfo info, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java index 69d6c9e0c3bd..32ebe2d6aecf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java @@ -33,6 +33,7 @@ import android.os.RemoteException; import android.util.Log; import android.view.SurfaceControl; import android.view.SyncRtSurfaceTransactionApplier; +import android.view.WindowManagerGlobal; import androidx.annotation.Nullable; @@ -143,7 +144,6 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis mSystemWindows.addView(mPipMenuView, getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */), 0, SHELL_ROOT_LAYER_PIP); - mPipMenuView.setFocusGrantToken(mSystemWindows.getFocusGrantToken(mPipMenuView)); } @Override @@ -164,6 +164,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis t.setPosition(menuSurfaceControl, menuBounds.left, menuBounds.top); t.apply(); } + grantPipMenuFocus(true); mPipMenuView.show(mInMoveMode, mDelegate.getPipGravity()); } } @@ -194,8 +195,9 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis if (DEBUG) Log.d(TAG, "hideMenu()"); } - mPipMenuView.hide(mInMoveMode); + mPipMenuView.hide(); if (!mInMoveMode) { + grantPipMenuFocus(false); mDelegate.closeMenu(); } } @@ -453,4 +455,15 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis void closePip(); } + + private void grantPipMenuFocus(boolean grantFocus) { + if (DEBUG) Log.d(TAG, "grantWindowFocus(" + grantFocus + ")"); + + try { + WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */, + mSystemWindows.getFocusGrantToken(mPipMenuView), grantFocus); + } catch (Exception e) { + Log.e(TAG, "Unable to update focus", e); + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java index 773e9bfa8977..3090139f6db9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java @@ -30,7 +30,6 @@ import android.app.RemoteAction; import android.content.Context; import android.graphics.Rect; import android.os.Handler; -import android.os.IBinder; import android.util.AttributeSet; import android.util.Log; import android.view.Gravity; @@ -39,7 +38,6 @@ import android.view.SurfaceControl; import android.view.View; import android.view.ViewGroup; import android.view.ViewRootImpl; -import android.view.WindowManagerGlobal; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; @@ -72,7 +70,6 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { private final ImageView mArrowRight; private final ImageView mArrowDown; private final ImageView mArrowLeft; - private IBinder mFocusGrantToken = null; private final ViewGroup mScrollView; private final ViewGroup mHorizontalScrollView; @@ -152,10 +149,6 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { mListener = listener; } - void setFocusGrantToken(IBinder token) { - mFocusGrantToken = token; - } - void setExpandedModeEnabled(boolean enabled) { mExpandButton.setVisibility(enabled ? VISIBLE : GONE); } @@ -170,8 +163,6 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { void show(boolean inMoveMode, int gravity) { if (DEBUG) Log.d(TAG, "show(), inMoveMode: " + inMoveMode); - grantWindowFocus(true); - if (inMoveMode) { showMovementHints(gravity); } else { @@ -180,15 +171,11 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { animateAlphaTo(1, mMenuFrameView); } - void hide(boolean isInMoveMode) { + void hide() { if (DEBUG) Log.d(TAG, "hide()"); animateAlphaTo(0, mActionButtonsContainer); animateAlphaTo(0, mMenuFrameView); hideMovementHints(); - - if (!isInMoveMode) { - grantWindowFocus(false); - } } private void animateAlphaTo(float alpha, View view) { @@ -217,17 +204,6 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { || mArrowLeft.getAlpha() != 0f; } - private void grantWindowFocus(boolean grantFocus) { - if (DEBUG) Log.d(TAG, "grantWindowFocus(" + grantFocus + ")"); - - try { - WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */, - mFocusGrantToken, grantFocus); - } catch (Exception e) { - Log.e(TAG, "Unable to update focus", e); - } - } - void setAdditionalActions(List<RemoteAction> actions, Handler mainHandler) { if (DEBUG) Log.d(TAG, "setAdditionalActions()"); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 81dacdb753a7..76641f0e6c21 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -979,7 +979,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, t.setAlpha(dividerLeash, 1); t.setLayer(dividerLeash, SPLIT_DIVIDER_LAYER); t.setPosition(dividerLeash, - mSplitLayout.getDividerBounds().left, mSplitLayout.getDividerBounds().top); + mSplitLayout.getRefDividerBounds().left, + mSplitLayout.getRefDividerBounds().top); } else { t.hide(dividerLeash); } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt index 65eb9aa991c2..57bcbc093a62 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt @@ -17,6 +17,7 @@ package com.android.wm.shell.flicker.apppairs import android.platform.test.annotations.Presubmit +import android.view.Display import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory @@ -24,6 +25,7 @@ import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.annotation.Group1 import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.traces.common.WindowManagerConditionsFactory import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd import com.android.wm.shell.flicker.helpers.AppPairsHelper import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig @@ -60,7 +62,18 @@ class AppPairsTestSupportPairNonResizeableApps( // TODO pair apps through normal UX flow executeShellCommand( composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true)) - nonResizeableApp?.run { wmHelper.waitForFullScreenApp(nonResizeableApp.component) } + val waitConditions = mutableListOf( + WindowManagerConditionsFactory.isWindowVisible(primaryApp.component), + WindowManagerConditionsFactory.isLayerVisible(primaryApp.component), + WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY)) + + nonResizeableApp?.let { + waitConditions.add( + WindowManagerConditionsFactory.isWindowVisible(nonResizeableApp.component)) + waitConditions.add( + WindowManagerConditionsFactory.isLayerVisible(nonResizeableApp.component)) + } + wmHelper.waitFor(*waitConditions.toTypedArray()) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt index 0f00edea136f..cc5b9f9eb26d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt @@ -62,7 +62,7 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper( if (wmHelper == null) { device.waitForIdle() } else { - require(wmHelper.waitImeShown()) { "IME did not appear" } + wmHelper.waitImeShown() } } @@ -79,7 +79,7 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper( if (wmHelper == null) { uiDevice.waitForIdle() } else { - require(wmHelper.waitImeGone()) { "IME did did not close" } + wmHelper.waitImeGone() } } else { // While pressing the back button should close the IME on TV as well, it may also lead diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt index 7e232ea32181..e9d438a569d5 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt @@ -58,17 +58,27 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( } } - /** {@inheritDoc} */ - override fun launchViaIntent( + /** + * Launches the app through an intent instead of interacting with the launcher and waits + * until the app window is in PIP mode + */ + @JvmOverloads + fun launchViaIntentAndWaitForPip( wmHelper: WindowManagerStateHelper, - expectedWindowName: String, - action: String?, + expectedWindowName: String = "", + action: String? = null, stringExtras: Map<String, String> ) { - super.launchViaIntent(wmHelper, expectedWindowName, action, stringExtras) - wmHelper.waitPipShown() + launchViaIntentAndWaitShown(wmHelper, expectedWindowName, action, stringExtras, + waitConditions = arrayOf(WindowManagerStateHelper.pipShownCondition)) } + /** + * Expand the PIP window back to full screen via intent and wait until the app is visible + */ + fun exitPipToFullScreenViaIntent(wmHelper: WindowManagerStateHelper) = + launchViaIntentAndWaitShown(wmHelper) + private fun focusOnObject(selector: BySelector): Boolean { // We expect all the focusable UI elements to be arranged in a way so that it is possible // to "cycle" over all them by clicking the D-Pad DOWN button, going back up to "the top" diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt index f2c50935a3e9..274d34ba3c5b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt @@ -64,7 +64,18 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { * Defines the transition used to run the test */ override val transition: FlickerBuilder.() -> Unit - get() = buildTransition(eachRun = true, stringExtras = emptyMap()) { + get() = { + setupAndTeardown(this) + setup { + eachRun { + pipApp.launchViaIntent(wmHelper) + } + } + teardown { + eachRun { + pipApp.exit(wmHelper) + } + } transitions { pipApp.clickEnterPipButton(wmHelper) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt index 4f98b70e222a..e2d08346efb6 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt @@ -74,7 +74,7 @@ class ExitPipViaExpandButtonClickTest( // This will bring PipApp to fullscreen pipApp.expandPipWindowToApp(wmHelper) // Wait until the other app is no longer visible - wmHelper.waitForSurfaceAppeared(testApp.component.toWindowName()) + wmHelper.waitForSurfaceAppeared(testApp.component) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt index e00d7491839f..3fe6f02eccf7 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt @@ -72,9 +72,9 @@ class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransit } transitions { // This will bring PipApp to fullscreen - pipApp.launchViaIntent(wmHelper) + pipApp.exitPipToFullScreenViaIntent(wmHelper) // Wait until the other app is no longer visible - wmHelper.waitForSurfaceAppeared(testApp.component.toWindowName()) + wmHelper.waitForWindowSurfaceDisappeared(testApp.component) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt index bb66f7bbc01f..654fa4ec9446 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt @@ -122,15 +122,14 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) { setup { test { - removeAllTasksButHome() if (!eachRun) { - pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras) + pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras) wmHelper.waitPipShown() } } eachRun { if (eachRun) { - pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras) + pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras) wmHelper.waitPipShown() } } @@ -145,7 +144,6 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) { if (!eachRun) { pipApp.exit(wmHelper) } - removeAllTasksButHome() } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java index dda1a8295e51..85527c81ad9e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java @@ -18,6 +18,7 @@ package com.android.wm.shell.splitscreen; import static android.view.Display.DEFAULT_DISPLAY; import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; + import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -51,6 +52,7 @@ public class SplitTestUtils { final SurfaceControl leash = createMockSurface(); SplitLayout out = mock(SplitLayout.class); doReturn(dividerBounds).when(out).getDividerBounds(); + doReturn(dividerBounds).when(out).getRefDividerBounds(); doReturn(leash).when(out).getDividerLeash(); return out; } diff --git a/packages/CompanionDeviceManager/Android.bp b/packages/CompanionDeviceManager/Android.bp index 6ded16371e8a..9f5bfd40e7e3 100644 --- a/packages/CompanionDeviceManager/Android.bp +++ b/packages/CompanionDeviceManager/Android.bp @@ -34,6 +34,7 @@ license { android_app { name: "CompanionDeviceManager", defaults: ["platform_app_defaults"], + certificate: "platform", srcs: ["src/**/*.java"], static_libs: [ diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml index 06f2d9d0f0c8..8b5d214f7a10 100644 --- a/packages/CompanionDeviceManager/AndroidManifest.xml +++ b/packages/CompanionDeviceManager/AndroidManifest.xml @@ -31,6 +31,7 @@ <uses-permission android:name="android.permission.RADIO_SCAN_WITHOUT_LOCATION"/> <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"/> + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/> <application android:allowClearUserData="true" diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java index b51d3103caec..a6a8fcf9af62 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java @@ -37,6 +37,7 @@ import android.companion.AssociationRequest; import android.companion.CompanionDeviceManager; import android.companion.IAssociationRequestCallback; import android.content.Intent; +import android.content.pm.PackageManager; import android.net.MacAddress; import android.os.Bundle; import android.os.Handler; @@ -71,6 +72,9 @@ public class CompanionDeviceActivity extends AppCompatActivity { private static final String EXTRA_ASSOCIATION_REQUEST = "association_request"; private static final String EXTRA_RESULT_RECEIVER = "result_receiver"; + // Activity result: Internal Error. + private static final int RESULT_INTERNAL_ERROR = 2; + // AssociationRequestsProcessor -> UI private static final int RESULT_CODE_ASSOCIATION_CREATED = 0; private static final String EXTRA_ASSOCIATION = "association"; @@ -191,6 +195,20 @@ public class CompanionDeviceActivity extends AppCompatActivity { private void initUI() { if (DEBUG) Log.d(TAG, "initUI(), request=" + mRequest); + final String packageName = mRequest.getPackageName(); + final int userId = mRequest.getUserId(); + final CharSequence appLabel; + + try { + appLabel = getApplicationLabel(this, packageName, userId); + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, "Package u" + userId + "/" + packageName + " not found."); + + CompanionDeviceDiscoveryService.stop(this); + setResultAndFinish(null, RESULT_INTERNAL_ERROR); + return; + } + setContentView(R.layout.activity_confirmation); mTitle = findViewById(R.id.title); @@ -203,8 +221,6 @@ public class CompanionDeviceActivity extends AppCompatActivity { mButtonAllow.setOnClickListener(this::onPositiveButtonClick); findViewById(R.id.btn_negative).setOnClickListener(this::onNegativeButtonClick); - final CharSequence appLabel = getApplicationLabel(this, mRequest.getPackageName()); - if (mRequest.isSelfManaged()) { initUiForSelfManagedAssociation(appLabel); } else if (mRequest.isSingleDevice()) { @@ -257,7 +273,7 @@ public class CompanionDeviceActivity extends AppCompatActivity { if (DEBUG) Log.i(TAG, "onAssociationCreated(), association=" + association); // Don't need to notify the app, CdmService has already done that. Just finish. - setResultAndFinish(association); + setResultAndFinish(association, RESULT_OK); } private void cancel(boolean discoveryTimeout) { @@ -284,10 +300,10 @@ public class CompanionDeviceActivity extends AppCompatActivity { } // ... then set result and finish ("sending" onActivityResult()). - setResultAndFinish(null); + setResultAndFinish(null, RESULT_CANCELED); } - private void setResultAndFinish(@Nullable AssociationInfo association) { + private void setResultAndFinish(@Nullable AssociationInfo association, int resultCode) { if (DEBUG) Log.i(TAG, "setResultAndFinish(), association=" + association); final Intent data = new Intent(); @@ -297,7 +313,7 @@ public class CompanionDeviceActivity extends AppCompatActivity { data.putExtra(CompanionDeviceManager.EXTRA_DEVICE, mSelectedDevice.getDevice()); } } - setResult(association != null ? RESULT_OK : RESULT_CANCELED, data); + setResult(resultCode, data); finish(); } diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java index eab421e48446..e3e563d56e8a 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java @@ -50,14 +50,13 @@ class Utils { } static @NonNull CharSequence getApplicationLabel( - @NonNull Context context, @NonNull String packageName) { + @NonNull Context context, @NonNull String packageName, int userId) + throws PackageManager.NameNotFoundException { final PackageManager packageManager = context.getPackageManager(); - final ApplicationInfo appInfo; - try { - appInfo = packageManager.getApplicationInfo(packageName, 0); - } catch (PackageManager.NameNotFoundException e) { - throw new RuntimeException(e); - } + + final ApplicationInfo appInfo = packageManager.getApplicationInfoAsUser( + packageName, PackageManager.ApplicationInfoFlags.of(0), userId); + return packageManager.getApplicationLabel(appInfo); } diff --git a/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java b/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java index 961fab32fc2c..4ed7e19f341d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java @@ -22,6 +22,7 @@ import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCK import android.content.ContentResolver; import android.content.Context; +import android.content.res.Resources; import android.database.ContentObserver; import android.os.Handler; import android.os.Looper; @@ -34,7 +35,10 @@ import android.util.SparseIntArray; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; +import java.util.Objects; import java.util.Set; /** @@ -48,13 +52,14 @@ public final class DeviceStateRotationLockSettingsManager { private static DeviceStateRotationLockSettingsManager sSingleton; - private final String[] mDeviceStateRotationLockDefaults; private final Handler mMainHandler = new Handler(Looper.getMainLooper()); private final Set<DeviceStateRotationLockSettingsListener> mListeners = new HashSet<>(); private final SecureSettings mSecureSettings; + private String[] mDeviceStateRotationLockDefaults; private SparseIntArray mDeviceStateRotationLockSettings; private SparseIntArray mDeviceStateRotationLockFallbackSettings; private String mLastSettingValue; + private List<SettableDeviceState> mSettableDeviceStates; @VisibleForTesting DeviceStateRotationLockSettingsManager(Context context, SecureSettings secureSettings) { @@ -79,6 +84,12 @@ public final class DeviceStateRotationLockSettingsManager { return sSingleton; } + /** Resets the singleton instance of this class. Only used for testing. */ + @VisibleForTesting + public static synchronized void resetInstance() { + sSingleton = null; + } + /** Returns true if device-state based rotation lock settings are enabled. */ public static boolean isDeviceStateRotationLockEnabled(Context context) { return context.getResources() @@ -186,6 +197,12 @@ public final class DeviceStateRotationLockSettingsManager { return true; } + /** Returns a list of device states and their respective auto-rotation setting availability. */ + public List<SettableDeviceState> getSettableDeviceStates() { + // Returning a copy to make sure that nothing outside can mutate our internal list. + return new ArrayList<>(mSettableDeviceStates); + } + private void initializeInMemoryMap() { String serializedSetting = mSecureSettings.getStringForUser( @@ -220,6 +237,17 @@ public final class DeviceStateRotationLockSettingsManager { } } + /** + * Resets the state of the class and saved settings back to the default values provided by the + * resources config. + */ + @VisibleForTesting + public void resetStateForTesting(Resources resources) { + mDeviceStateRotationLockDefaults = + resources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults); + fallbackOnDefaults(); + } + private void fallbackOnDefaults() { loadDefaults(); persistSettings(); @@ -259,6 +287,7 @@ public final class DeviceStateRotationLockSettingsManager { } private void loadDefaults() { + mSettableDeviceStates = new ArrayList<>(mDeviceStateRotationLockDefaults.length); mDeviceStateRotationLockSettings = new SparseIntArray( mDeviceStateRotationLockDefaults.length); mDeviceStateRotationLockFallbackSettings = new SparseIntArray(1); @@ -279,6 +308,8 @@ public final class DeviceStateRotationLockSettingsManager { + values.length); } } + boolean isSettable = rotationLockSetting != DEVICE_STATE_ROTATION_LOCK_IGNORED; + mSettableDeviceStates.add(new SettableDeviceState(deviceState, isSettable)); mDeviceStateRotationLockSettings.put(deviceState, rotationLockSetting); } catch (NumberFormatException e) { Log.wtf(TAG, "Error parsing settings entry. Entry was: " + entry, e); @@ -308,4 +339,38 @@ public final class DeviceStateRotationLockSettingsManager { /** Called whenever the settings have changed. */ void onSettingsChanged(); } + + /** Represents a device state and whether it has an auto-rotation setting. */ + public static class SettableDeviceState { + private final int mDeviceState; + private final boolean mIsSettable; + + SettableDeviceState(int deviceState, boolean isSettable) { + mDeviceState = deviceState; + mIsSettable = isSettable; + } + + /** Returns the device state associated with this object. */ + public int getDeviceState() { + return mDeviceState; + } + + /** Returns whether there is an auto-rotation setting for this device state. */ + public boolean isSettable() { + return mIsSettable; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SettableDeviceState)) return false; + SettableDeviceState that = (SettableDeviceState) o; + return mDeviceState == that.mDeviceState && mIsSettable == that.mIsSettable; + } + + @Override + public int hashCode() { + return Objects.hash(mDeviceState, mIsSettable); + } + } } diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java index 1a45384bc768..81006dd6b011 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java @@ -30,12 +30,17 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.R; +import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager.SettableDeviceState; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.List; + @SmallTest @RunWith(AndroidJUnit4.class) public class DeviceStateRotationLockSettingsManagerTest { @@ -94,4 +99,22 @@ public class DeviceStateRotationLockSettingsManagerTest { assertThat(mNumSettingsChanges).isEqualTo(3); } + + @Test + public void getSettableDeviceStates_returnsExpectedValuesInOriginalOrder() { + when(mMockResources.getStringArray( + R.array.config_perDeviceStateRotationLockDefaults)).thenReturn( + new String[]{"2:2", "4:0", "1:1", "0:0"}); + + List<SettableDeviceState> settableDeviceStates = + DeviceStateRotationLockSettingsManager.getInstance( + mMockContext).getSettableDeviceStates(); + + assertThat(settableDeviceStates).containsExactly( + new SettableDeviceState(/* deviceState= */ 2, /* isSettable= */ true), + new SettableDeviceState(/* deviceState= */ 4, /* isSettable= */ false), + new SettableDeviceState(/* deviceState= */ 1, /* isSettable= */ true), + new SettableDeviceState(/* deviceState= */ 0, /* isSettable= */ false) + ).inOrder(); + } } diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index f0b180e5cab6..121f9e58a62a 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -588,6 +588,9 @@ <uses-permission android:name="android.permission.MANAGE_HOTWORD_DETECTION" /> <uses-permission android:name="android.permission.BIND_HOTWORD_DETECTION_SERVICE" /> + <!-- Permission required for CTS test - KeyguardLockedStateApiTest --> + <uses-permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" /> + <uses-permission android:name="android.permission.MANAGE_APP_HIBERNATION"/> <!-- Permission required for CTS test - ResourceObserverNativeTest --> diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java index 0d20403b08f2..de03993a6f17 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java @@ -43,6 +43,7 @@ import android.os.Handler; import android.os.RemoteException; import android.util.Log; import android.util.Range; +import android.util.Size; import android.view.Choreographer; import android.view.Display; import android.view.Gravity; @@ -62,6 +63,8 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.accessibility.IRemoteMagnificationAnimationCallback; +import androidx.core.math.MathUtils; + import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.systemui.R; @@ -166,6 +169,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold private final Rect mMagnificationFrameBoundary = new Rect(); // The top Y of the system gesture rect at the bottom. Set to -1 if it is invalid. private int mSystemGestureTop = -1; + private int mMinWindowSize; private final WindowMagnificationAnimationController mAnimationController; private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider; @@ -208,8 +212,10 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mBounceEffectDuration = mResources.getInteger( com.android.internal.R.integer.config_shortAnimTime); updateDimensions(); - setMagnificationFrameWith(mWindowBounds, mWindowBounds.width() / 2, - mWindowBounds.height() / 2); + + final Size windowSize = getDefaultWindowSizeWithWindowBounds(mWindowBounds); + setMagnificationFrame(windowSize.getWidth(), windowSize.getHeight(), + mWindowBounds.width() / 2, mWindowBounds.height() / 2); computeBounceAnimationScale(); mMirrorWindowControl = mirrorWindowControl; @@ -281,6 +287,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold R.dimen.magnification_drag_view_size); mOuterBorderSize = mResources.getDimensionPixelSize( R.dimen.magnification_outer_border_margin); + mMinWindowSize = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.accessibility_window_magnifier_min_size); } private void computeBounceAnimationScale() { @@ -414,9 +422,12 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold return false; } mWindowBounds.set(currentWindowBounds); + final Size windowSize = getDefaultWindowSizeWithWindowBounds(mWindowBounds); final float newCenterX = (getCenterX()) * mWindowBounds.width() / oldWindowBounds.width(); final float newCenterY = (getCenterY()) * mWindowBounds.height() / oldWindowBounds.height(); - setMagnificationFrameWith(mWindowBounds, (int) newCenterX, (int) newCenterY); + + setMagnificationFrame(windowSize.getWidth(), windowSize.getHeight(), (int) newCenterX, + (int) newCenterY); calculateMagnificationFrameBoundary(); return true; } @@ -454,11 +465,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mWindowBounds.set(currentWindowBounds); - calculateMagnificationFrameBoundary(); - - if (!isWindowVisible()) { - return; - } // Keep MirrorWindow position on the screen unchanged when device rotates 90° // clockwise or anti-clockwise. @@ -469,14 +475,13 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold } else if (rotationDegree == 270) { matrix.postTranslate(0, mWindowBounds.height()); } - // The rect of MirrorView is going to be transformed. - LayoutParams params = - (LayoutParams) mMirrorView.getLayoutParams(); - mTmpRect.set(params.x, params.y, params.x + params.width, params.y + params.height); - final RectF transformedRect = new RectF(mTmpRect); + + final RectF transformedRect = new RectF(mMagnificationFrame); + // The window frame is going to be transformed by the rotation matrix. + transformedRect.inset(-mMirrorSurfaceMargin, -mMirrorSurfaceMargin); matrix.mapRect(transformedRect); - moveWindowMagnifier(transformedRect.left - mTmpRect.left, - transformedRect.top - mTmpRect.top); + setWindowSizeAndCenter((int) transformedRect.width(), (int) transformedRect.height(), + (int) transformedRect.centerX(), (int) transformedRect.centerY()); } /** Returns the rotation degree change of two {@link Surface.Rotation} */ @@ -573,16 +578,52 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold } } - private void setMagnificationFrameWith(Rect windowBounds, int centerX, int centerY) { + /** + * Sets the window size with given width and height in pixels without changing the + * window center. The width or the height will be clamped in the range + * [{@link #mMinWindowSize}, screen width or height]. + * + * @param width the window width in pixels + * @param height the window height in pixels. + */ + public void setWindowSize(int width, int height) { + setWindowSizeAndCenter(width, height, Float.NaN, Float.NaN); + } + + void setWindowSizeAndCenter(int width, int height, float centerX, float centerY) { + width = MathUtils.clamp(width, mMinWindowSize, mWindowBounds.width()); + height = MathUtils.clamp(height, mMinWindowSize, mWindowBounds.height()); + + if (Float.isNaN(centerX)) { + centerX = mMagnificationFrame.centerX(); + } + if (Float.isNaN(centerX)) { + centerY = mMagnificationFrame.centerY(); + } + + final int frameWidth = width - 2 * mMirrorSurfaceMargin; + final int frameHeight = height - 2 * mMirrorSurfaceMargin; + setMagnificationFrame(frameWidth, frameHeight, (int) centerX, (int) centerY); + calculateMagnificationFrameBoundary(); + // Correct the frame position to ensure it is inside the boundary. + updateMagnificationFramePosition(0, 0); + modifyWindowMagnification(true); + } + + private void setMagnificationFrame(int width, int height, int centerX, int centerY) { // Sets the initial frame area for the mirror and place it to the given center on the // display. + final int initX = centerX - width / 2; + final int initY = centerY - height / 2; + mMagnificationFrame.set(initX, initY, initX + width, initY + height); + } + + private Size getDefaultWindowSizeWithWindowBounds(Rect windowBounds) { int initSize = Math.min(windowBounds.width(), windowBounds.height()) / 2; initSize = Math.min(mResources.getDimensionPixelSize(R.dimen.magnification_max_frame_size), initSize); initSize += 2 * mMirrorSurfaceMargin; - final int initX = centerX - initSize / 2; - final int initY = centerY - initSize / 2; - mMagnificationFrame.set(initX, initY, initX + initSize, initY + initSize); + return new Size(initSize, initSize); } /** @@ -596,8 +637,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold } mTransaction.show(mMirrorSurface) .reparent(mMirrorSurface, mMirrorSurfaceView.getSurfaceControl()); - - modifyWindowMagnification(mTransaction); + modifyWindowMagnification(false); } private void addDragTouchListeners() { @@ -615,18 +655,25 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold } /** - * Modifies the placement of the mirrored content when the position of mMirrorView is updated. + * Modifies the placement of the mirrored content when the position or size of mMirrorView is + * updated. + * + * @param computeWindowSize set to {@code true} to compute window size with + * {@link #mMagnificationFrame}. */ - private void modifyWindowMagnification(SurfaceControl.Transaction t) { + private void modifyWindowMagnification(boolean computeWindowSize) { mSfVsyncFrameProvider.postFrameCallback(mMirrorViewGeometryVsyncCallback); - updateMirrorViewLayout(); + updateMirrorViewLayout(computeWindowSize); } /** - * Updates the layout params of MirrorView and translates MirrorView position when the view is - * moved close to the screen edges. + * Updates the layout params of MirrorView based on the size of {@link #mMagnificationFrame} + * and translates MirrorView position when the view is moved close to the screen edges; + * + * @param computeWindowSize set to {@code true} to compute window size with + * {@link #mMagnificationFrame}. */ - private void updateMirrorViewLayout() { + private void updateMirrorViewLayout(boolean computeWindowSize) { if (!isWindowVisible()) { return; } @@ -637,6 +684,10 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold (LayoutParams) mMirrorView.getLayoutParams(); params.x = mMagnificationFrame.left - mMirrorSurfaceMargin; params.y = mMagnificationFrame.top - mMirrorSurfaceMargin; + if (computeWindowSize) { + params.width = mMagnificationFrame.width() + 2 * mMirrorSurfaceMargin; + params.height = mMagnificationFrame.height() + 2 * mMirrorSurfaceMargin; + } // Translates MirrorView position to make MirrorSurfaceView that is inside MirrorView // able to move close to the screen edges. @@ -899,7 +950,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold createMirrorWindow(); showControls(); } else { - modifyWindowMagnification(mTransaction); + modifyWindowMagnification(false); } } @@ -930,7 +981,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold return; } if (updateMagnificationFramePosition((int) offsetX, (int) offsetY)) { - modifyWindowMagnification(mTransaction); + modifyWindowMagnification(false); } } @@ -1014,9 +1065,15 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold pw.println(" mOverlapWithGestureInsets:" + mOverlapWithGestureInsets); pw.println(" mScale:" + mScale); pw.println(" mMirrorViewBounds:" + (isWindowVisible() ? mMirrorViewBounds : "empty")); + pw.println(" mMagnificationFrameBoundary:" + + (isWindowVisible() ? mMagnificationFrameBoundary : "empty")); + pw.println(" mMagnificationFrame:" + + (isWindowVisible() ? mMagnificationFrame : "empty")); pw.println(" mSourceBounds:" + (isWindowVisible() ? mSourceBounds : "empty")); pw.println(" mSystemGestureTop:" + mSystemGestureTop); + pw.println(" mMagnificationFrameOffsetX:" + mMagnificationFrameOffsetX); + pw.println(" mMagnificationFrameOffsetY:" + mMagnificationFrameOffsetY); } private class MirrorWindowA11yDelegate extends View.AccessibilityDelegate { diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt index a262b8ad2e28..217210a5dafc 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt @@ -165,6 +165,7 @@ internal class FooterActionsController @Inject constructor( powerMenuLite.visibility = View.GONE } settingsButton.setOnClickListener(onClickListener) + multiUserSetting.isListening = true if (featureFlags.isEnabled(Flags.NEW_FOOTER)) { val securityFooter = securityFooterController.view as DualHeightHorizontalLinearLayout securityFootersContainer?.addView(securityFooter) @@ -215,6 +216,7 @@ internal class FooterActionsController @Inject constructor( override fun onViewDetached() { setListening(false) + multiUserSetting.isListening = false } fun setListening(listening: Boolean) { @@ -222,7 +224,6 @@ internal class FooterActionsController @Inject constructor( return } this.listening = listening - multiUserSetting.isListening = listening if (this.listening) { userInfoController.addCallback(onUserInfoChangedListener) updateView() diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java index 1dd5e227a909..6e5926db519d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java @@ -88,6 +88,7 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; @LargeTest @TestableLooper.RunWithLooper @@ -345,15 +346,17 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { @Test public void onOrientationChanged_disabled_updateDisplayRotation() { - final Display display = Mockito.spy(mContext.getDisplay()); - when(display.getRotation()).thenReturn(Surface.ROTATION_90); - when(mContext.getDisplay()).thenReturn(display); + final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds()); + // Rotate the window clockwise 90 degree. + windowBounds.set(windowBounds.top, windowBounds.left, windowBounds.bottom, + windowBounds.right); + mWindowManager.setWindowBounds(windowBounds); + final int newRotation = simulateRotateTheDevice(); - mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION); - }); + mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.onConfigurationChanged( + ActivityInfo.CONFIG_ORIENTATION)); - assertEquals(Surface.ROTATION_90, mWindowMagnificationController.mRotation); + assertEquals(newRotation, mWindowMagnificationController.mRotation); } @Test @@ -603,6 +606,113 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { ReferenceTestUtils.waitForCondition(() -> hasMagnificationOverlapFlag()); } + @Test + public void setMinimumWindowSize_enabled_expectedWindowSize() { + final int minimumWindowSize = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.accessibility_window_magnifier_min_size); + final int expectedWindowHeight = minimumWindowSize; + final int expectedWindowWidth = minimumWindowSize; + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, Float.NaN)); + + final AtomicInteger actualWindowHeight = new AtomicInteger(); + final AtomicInteger actualWindowWidth = new AtomicInteger(); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight); + actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height); + actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width); + + }); + + assertEquals(expectedWindowHeight, actualWindowHeight.get()); + assertEquals(expectedWindowWidth, actualWindowWidth.get()); + } + + @Test + public void setMinimumWindowSizeThenEnable_expectedWindowSize() { + final int minimumWindowSize = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.accessibility_window_magnifier_min_size); + final int expectedWindowHeight = minimumWindowSize; + final int expectedWindowWidth = minimumWindowSize; + + final AtomicInteger actualWindowHeight = new AtomicInteger(); + final AtomicInteger actualWindowWidth = new AtomicInteger(); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight); + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, Float.NaN); + actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height); + actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width); + }); + + assertEquals(expectedWindowHeight, actualWindowHeight.get()); + assertEquals(expectedWindowWidth, actualWindowWidth.get()); + } + + @Test + public void setWindowSizeLessThanMin_enabled_minimumWindowSize() { + final int minimumWindowSize = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.accessibility_window_magnifier_min_size); + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, Float.NaN)); + + final AtomicInteger actualWindowHeight = new AtomicInteger(); + final AtomicInteger actualWindowWidth = new AtomicInteger(); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.setWindowSize(minimumWindowSize - 10, + minimumWindowSize - 10); + actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height); + actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width); + }); + + assertEquals(minimumWindowSize, actualWindowHeight.get()); + assertEquals(minimumWindowSize, actualWindowWidth.get()); + } + + @Test + public void setWindowSizeLargerThanScreenSize_enabled_windowSizeIsScreenSize() { + final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds(); + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, Float.NaN)); + + final AtomicInteger actualWindowHeight = new AtomicInteger(); + final AtomicInteger actualWindowWidth = new AtomicInteger(); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.setWindowSize(bounds.width() + 10, bounds.height() + 10); + actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height); + actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width); + }); + + assertEquals(bounds.height(), actualWindowHeight.get()); + assertEquals(bounds.width(), actualWindowWidth.get()); + } + + @Test + public void setWindowCenterOutOfScreen_enabled_magnificationCenterIsInsideTheScreen() { + + final int minimumWindowSize = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.accessibility_window_magnifier_min_size); + final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds(); + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, Float.NaN)); + + final AtomicInteger magnificationCenterX = new AtomicInteger(); + final AtomicInteger magnificationCenterY = new AtomicInteger(); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.setWindowSizeAndCenter(minimumWindowSize, + minimumWindowSize, bounds.right, bounds.bottom); + magnificationCenterX.set((int) mWindowMagnificationController.getCenterX()); + magnificationCenterY.set((int) mWindowMagnificationController.getCenterY()); + }); + + assertTrue(magnificationCenterX.get() < bounds.right); + assertTrue(magnificationCenterY.get() < bounds.bottom); + } + private CharSequence getAccessibilityWindowTitle() { final View mirrorView = mWindowManager.getAttachedView(); if (mirrorView == null) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java index 58ffbfa76328..15aaf5fa8e42 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java @@ -163,6 +163,31 @@ public class DreamOverlayServiceTest extends SysuiTestCase { } @Test + public void testPreviewModeFalseByDefault() { + mService.onBind(new Intent()); + + assertThat(mService.isPreviewMode()).isFalse(); + } + + @Test + public void testPreviewModeSetByIntentExtra() { + final Intent intent = new Intent(); + intent.putExtra(DreamService.EXTRA_IS_PREVIEW, true); + mService.onBind(intent); + + assertThat(mService.isPreviewMode()).isTrue(); + } + + @Test + public void testDreamLabel() { + final Intent intent = new Intent(); + intent.putExtra(DreamService.EXTRA_DREAM_LABEL, "TestDream"); + mService.onBind(intent); + + assertThat(mService.getDreamLabel()).isEqualTo("TestDream"); + } + + @Test public void testDestroy() { mService.onDestroy(); mMainExecutor.runAllReady(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt index 8ce50a680e50..f736f26e5c46 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt @@ -100,7 +100,9 @@ class FooterActionsControllerTest : LeakCheckedTest() { @After fun tearDown() { - ViewUtils.detachView(view) + if (view.isAttachedToWindow) { + ViewUtils.detachView(view) + } } @Test @@ -139,8 +141,7 @@ class FooterActionsControllerTest : LeakCheckedTest() { @Test fun testMultiUserSwitchUpdatedWhenSettingChanged() { - // When expanded, listening is true - controller.setListening(true) + // Always listening to setting while View is attached testableLooper.processAllMessages() val multiUserSwitch = view.requireViewById<View>(R.id.multi_user_switch) @@ -156,4 +157,24 @@ class FooterActionsControllerTest : LeakCheckedTest() { assertThat(multiUserSwitch.visibility).isEqualTo(View.VISIBLE) } + + @Test + fun testMultiUserSettingNotListenedAfterDetach() { + testableLooper.processAllMessages() + + val multiUserSwitch = view.requireViewById<View>(R.id.multi_user_switch) + assertThat(multiUserSwitch.visibility).isNotEqualTo(View.VISIBLE) + + ViewUtils.detachView(view) + + // The setting is only used as an indicator for whether the view should refresh. The actual + // value of the setting is ignored; isMultiUserEnabled is the source of truth + whenever(multiUserSwitchController.isMultiUserEnabled).thenReturn(true) + + // Changing the value of USER_SWITCHER_ENABLED should cause the view to update + fakeSettings.putIntForUser(Settings.Global.USER_SWITCHER_ENABLED, 1, userTracker.userId) + testableLooper.processAllMessages() + + assertThat(multiUserSwitch.visibility).isNotEqualTo(View.VISIBLE) + } }
\ No newline at end of file diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 6813f3f83135..1c10304f7223 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -15623,24 +15623,20 @@ public class ActivityManagerService extends IActivityManager.Stub } for (int i = 0, size = processes.size(); i < size; i++) { ProcessRecord app = processes.get(i); - pw.println(String.format("------ DUMP RESOURCES %s (%s) ------", + pw.println(String.format("Resources History for %s (%s)", app.processName, app.info.packageName)); pw.flush(); try { - TransferPipe tp = new TransferPipe(); + TransferPipe tp = new TransferPipe(" "); try { IApplicationThread thread = app.getThread(); if (thread != null) { app.getThread().dumpResources(tp.getWriteFd(), null); tp.go(fd.getFileDescriptor(), 2000); - pw.println(String.format("------ END DUMP RESOURCES %s (%s) ------", - app.processName, - app.info.packageName)); - pw.flush(); } else { pw.println(String.format( - "------ DUMP RESOURCES %s (%s) failed, no thread ------", + " Resources history for %s (%s) failed, no thread", app.processName, app.info.packageName)); } @@ -15648,11 +15644,7 @@ public class ActivityManagerService extends IActivityManager.Stub tp.kill(); } } catch (IOException e) { - pw.println(String.format( - "------ EXCEPTION DUMPING RESOURCES for %s (%s): %s ------", - app.processName, - app.info.packageName, - e.getMessage())); + pw.println(" " + e.getMessage()); pw.flush(); } diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java index b813bc48118a..0edbea0dbd28 100644 --- a/services/core/java/com/android/server/app/GameManagerService.java +++ b/services/core/java/com/android/server/app/GameManagerService.java @@ -90,6 +90,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.compat.CompatibilityOverrideConfig; import com.android.internal.compat.IPlatformCompat; import com.android.internal.os.BackgroundThread; +import com.android.internal.util.FrameworkStatsLog; import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.SystemService; @@ -268,16 +269,34 @@ public final class GameManagerService extends IGameManagerService.Stub { break; } case SET_GAME_STATE: { - if (mPowerManagerInternal == null) { - final Bundle data = msg.getData(); - Slog.d(TAG, "Error setting loading mode for package " - + data.getString(PACKAGE_NAME_MSG_KEY) - + " and userId " + data.getInt(USER_ID_MSG_KEY)); - break; - } final GameState gameState = (GameState) msg.obj; final boolean isLoading = gameState.isLoading(); - mPowerManagerInternal.setPowerMode(Mode.GAME_LOADING, isLoading); + final Bundle data = msg.getData(); + final String packageName = data.getString(PACKAGE_NAME_MSG_KEY); + final int userId = data.getInt(USER_ID_MSG_KEY); + + // Restrict to games only. Requires performance mode to be enabled. + final boolean boostEnabled = + getGameMode(packageName, userId) == GameManager.GAME_MODE_PERFORMANCE; + int uid; + try { + uid = mPackageManager.getPackageUidAsUser(packageName, userId); + } catch (NameNotFoundException e) { + Slog.v(TAG, "Failed to get package metadata"); + uid = -1; + } + FrameworkStatsLog.write(FrameworkStatsLog.GAME_STATE_CHANGED, packageName, uid, + boostEnabled, gameStateModeToStatsdGameState(gameState.getMode()), + isLoading, gameState.getLabel(), gameState.getQuality()); + + if (boostEnabled) { + if (mPowerManagerInternal == null) { + Slog.d(TAG, "Error setting loading mode for package " + packageName + + " and userId " + userId); + break; + } + mPowerManagerInternal.setPowerMode(Mode.GAME_LOADING, isLoading); + } break; } } @@ -387,12 +406,6 @@ public final class GameManagerService extends IGameManagerService.Stub { // Restrict to games only. return; } - - if (getGameMode(packageName, userId) != GameManager.GAME_MODE_PERFORMANCE) { - // Requires performance mode to be enabled. - return; - } - final Message msg = mHandler.obtainMessage(SET_GAME_STATE); final Bundle data = new Bundle(); data.putString(PACKAGE_NAME_MSG_KEY, packageName); @@ -1543,6 +1556,22 @@ public final class GameManagerService extends IGameManagerService.Stub { return out.toString(); } + private static int gameStateModeToStatsdGameState(int mode) { + switch (mode) { + case GameState.MODE_NONE: + return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_NONE; + case GameState.MODE_GAMEPLAY_INTERRUPTIBLE: + return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_GAMEPLAY_INTERRUPTIBLE; + case GameState.MODE_GAMEPLAY_UNINTERRUPTIBLE: + return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_GAMEPLAY_UNINTERRUPTIBLE; + case GameState.MODE_CONTENT: + return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_CONTENT; + case GameState.MODE_UNKNOWN: + default: + return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_UNKNOWN; + } + } + private static ServiceThread createServiceThread() { ServiceThread handlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/); diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index 3491cd59ebb7..49a935ebadc9 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -479,6 +479,8 @@ public class BtHelper { } if (profile == BluetoothProfile.A2DP) { mA2dp = (BluetoothA2dp) proxy; + } else if (profile == BluetoothProfile.HEARING_AID) { + mHearingAid = (BluetoothHearingAid) proxy; } else if (profile == BluetoothProfile.LE_AUDIO) { mLeAudio = (BluetoothLeAudio) proxy; } diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java index 76754d3e95d5..4a1a950c6a07 100644 --- a/services/core/java/com/android/server/dreams/DreamController.java +++ b/services/core/java/com/android/server/dreams/DreamController.java @@ -104,7 +104,7 @@ final class DreamController { pw.println(" mCurrentDream:"); pw.println(" mToken=" + mCurrentDream.mToken); pw.println(" mName=" + mCurrentDream.mName); - pw.println(" mIsTest=" + mCurrentDream.mIsTest); + pw.println(" mIsPreviewMode=" + mCurrentDream.mIsPreviewMode); pw.println(" mCanDoze=" + mCurrentDream.mCanDoze); pw.println(" mUserId=" + mCurrentDream.mUserId); pw.println(" mBound=" + mCurrentDream.mBound); @@ -117,7 +117,7 @@ final class DreamController { } public void startDream(Binder token, ComponentName name, - boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock, + boolean isPreviewMode, boolean canDoze, int userId, PowerManager.WakeLock wakeLock, ComponentName overlayComponentName) { stopDream(true /*immediate*/, "starting new dream"); @@ -127,10 +127,10 @@ final class DreamController { mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL); Slog.i(TAG, "Starting dream: name=" + name - + ", isTest=" + isTest + ", canDoze=" + canDoze + + ", isPreviewMode=" + isPreviewMode + ", canDoze=" + canDoze + ", userId=" + userId); - mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId, wakeLock); + mCurrentDream = new DreamRecord(token, name, isPreviewMode, canDoze, userId, wakeLock); mDreamStartTime = SystemClock.elapsedRealtime(); MetricsLogger.visible(mContext, @@ -140,6 +140,7 @@ final class DreamController { intent.setComponent(name); intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); intent.putExtra(DreamService.EXTRA_DREAM_OVERLAY_COMPONENT, overlayComponentName); + intent.putExtra(DreamService.EXTRA_IS_PREVIEW, isPreviewMode); try { if (!mContext.bindServiceAsUser(intent, mCurrentDream, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, @@ -190,7 +191,8 @@ final class DreamController { final DreamRecord oldDream = mCurrentDream; mCurrentDream = null; Slog.i(TAG, "Stopping dream: name=" + oldDream.mName - + ", isTest=" + oldDream.mIsTest + ", canDoze=" + oldDream.mCanDoze + + ", isPreviewMode=" + oldDream.mIsPreviewMode + + ", canDoze=" + oldDream.mCanDoze + ", userId=" + oldDream.mUserId + ", reason='" + reason + "'" + (mSavedStopReason == null ? "" : "(from '" + mSavedStopReason + "')")); @@ -247,7 +249,7 @@ final class DreamController { mCurrentDream.mService = service; - if (!mCurrentDream.mIsTest) { + if (!mCurrentDream.mIsPreviewMode) { mContext.sendBroadcastAsUser(mDreamingStartedIntent, UserHandle.ALL); mCurrentDream.mSentStartBroadcast = true; } @@ -263,7 +265,7 @@ final class DreamController { private final class DreamRecord implements DeathRecipient, ServiceConnection { public final Binder mToken; public final ComponentName mName; - public final boolean mIsTest; + public final boolean mIsPreviewMode; public final boolean mCanDoze; public final int mUserId; @@ -275,11 +277,11 @@ final class DreamController { public boolean mWakingGently; - public DreamRecord(Binder token, ComponentName name, - boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock) { + DreamRecord(Binder token, ComponentName name, boolean isPreviewMode, + boolean canDoze, int userId, PowerManager.WakeLock wakeLock) { mToken = token; mName = name; - mIsTest = isTest; + mIsPreviewMode = isPreviewMode; mCanDoze = canDoze; mUserId = userId; mWakeLock = wakeLock; diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index f0a6af3c8834..22d32a665611 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -87,7 +87,7 @@ public final class DreamManagerService extends SystemService { private Binder mCurrentDreamToken; private ComponentName mCurrentDreamName; private int mCurrentDreamUserId; - private boolean mCurrentDreamIsTest; + private boolean mCurrentDreamIsPreview; private boolean mCurrentDreamCanDoze; private boolean mCurrentDreamIsDozing; private boolean mCurrentDreamIsWaking; @@ -169,7 +169,7 @@ public final class DreamManagerService extends SystemService { pw.println("mCurrentDreamToken=" + mCurrentDreamToken); pw.println("mCurrentDreamName=" + mCurrentDreamName); pw.println("mCurrentDreamUserId=" + mCurrentDreamUserId); - pw.println("mCurrentDreamIsTest=" + mCurrentDreamIsTest); + pw.println("mCurrentDreamIsPreview=" + mCurrentDreamIsPreview); pw.println("mCurrentDreamCanDoze=" + mCurrentDreamCanDoze); pw.println("mCurrentDreamIsDozing=" + mCurrentDreamIsDozing); pw.println("mCurrentDreamIsWaking=" + mCurrentDreamIsWaking); @@ -190,7 +190,7 @@ public final class DreamManagerService extends SystemService { private boolean isDreamingInternal() { synchronized (mLock) { - return mCurrentDreamToken != null && !mCurrentDreamIsTest + return mCurrentDreamToken != null && !mCurrentDreamIsPreview && !mCurrentDreamIsWaking; } } @@ -235,7 +235,7 @@ public final class DreamManagerService extends SystemService { private void testDreamInternal(ComponentName dream, int userId) { synchronized (mLock) { - startDreamLocked(dream, true /*isTest*/, false /*canDoze*/, userId); + startDreamLocked(dream, true /*isPreviewMode*/, false /*canDoze*/, userId); } } @@ -244,7 +244,7 @@ public final class DreamManagerService extends SystemService { final ComponentName dream = chooseDreamForUser(doze, userId); if (dream != null) { synchronized (mLock) { - startDreamLocked(dream, false /*isTest*/, doze, userId); + startDreamLocked(dream, false /*isPreviewMode*/, doze, userId); } } } @@ -395,10 +395,10 @@ public final class DreamManagerService extends SystemService { } private void startDreamLocked(final ComponentName name, - final boolean isTest, final boolean canDoze, final int userId) { + final boolean isPreviewMode, final boolean canDoze, final int userId) { if (!mCurrentDreamIsWaking && Objects.equals(mCurrentDreamName, name) - && mCurrentDreamIsTest == isTest + && mCurrentDreamIsPreview == isPreviewMode && mCurrentDreamCanDoze == canDoze && mCurrentDreamUserId == userId) { Slog.i(TAG, "Already in target dream."); @@ -412,7 +412,7 @@ public final class DreamManagerService extends SystemService { final Binder newToken = new Binder(); mCurrentDreamToken = newToken; mCurrentDreamName = name; - mCurrentDreamIsTest = isTest; + mCurrentDreamIsPreview = isPreviewMode; mCurrentDreamCanDoze = canDoze; mCurrentDreamUserId = userId; @@ -424,7 +424,7 @@ public final class DreamManagerService extends SystemService { .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "startDream"); mHandler.post(wakeLock.wrap(() -> { mAtmInternal.notifyDreamStateChanged(true); - mController.startDream(newToken, name, isTest, canDoze, userId, wakeLock, + mController.startDream(newToken, name, isPreviewMode, canDoze, userId, wakeLock, mDreamOverlayServiceName); })); } @@ -457,7 +457,7 @@ public final class DreamManagerService extends SystemService { } mCurrentDreamToken = null; mCurrentDreamName = null; - mCurrentDreamIsTest = false; + mCurrentDreamIsPreview = false; mCurrentDreamCanDoze = false; mCurrentDreamUserId = 0; mCurrentDreamIsWaking = false; diff --git a/services/core/java/com/android/server/inputmethod/ImePlatformCompatUtils.java b/services/core/java/com/android/server/inputmethod/ImePlatformCompatUtils.java new file mode 100644 index 000000000000..83ca16d72c9b --- /dev/null +++ b/services/core/java/com/android/server/inputmethod/ImePlatformCompatUtils.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.inputmethod; + +import static android.inputmethodservice.InputMethodService.FINISH_INPUT_NO_FALLBACK_CONNECTION; +import static android.view.inputmethod.InputMethodManager.CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING; + +import android.content.Context; +import android.os.RemoteException; +import android.os.ServiceManager; + +import com.android.internal.compat.IPlatformCompat; + +/** + * A utility class used by {@link InputMethodManagerService} to manage the platform + * compatibility changes on IMF (Input Method Framework) side. + */ +final class ImePlatformCompatUtils { + private final IPlatformCompat mPlatformCompat = IPlatformCompat.Stub.asInterface( + ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); + + /** + * Whether to finish the {@link android.view.inputmethod.InputConnection} when the device + * becomes {@link android.os.PowerManager#isInteractive non-interactive}. + * + * @param imeUid The uid of the IME application + */ + public boolean shouldFinishInputWithReportToIme(int imeUid) { + return isChangeEnabledByUid(FINISH_INPUT_NO_FALLBACK_CONNECTION, imeUid); + } + + /** + * Whether to clear {@link android.view.inputmethod.InputMethodManager#SHOW_FORCED} flag + * when the next IME focused application changed. + * + * @param clientUid The uid of the app + */ + public boolean shouldClearShowForcedFlag(int clientUid) { + return isChangeEnabledByUid(CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING, clientUid); + } + + private boolean isChangeEnabledByUid(long changeFlag, int uid) { + boolean result = false; + try { + result = mPlatformCompat.isChangeEnabledByUid(changeFlag, uid); + } catch (RemoteException e) { + } + return result; + } +} diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 0b7e39136feb..eb1de2a9bf91 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -15,7 +15,6 @@ package com.android.server.inputmethod; -import static android.inputmethodservice.InputMethodService.FINISH_INPUT_NO_FALLBACK_CONNECTION; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; import static android.os.IServiceManager.DUMP_FLAG_PROTO; @@ -148,7 +147,6 @@ import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; import com.android.internal.annotations.GuardedBy; -import com.android.internal.compat.IPlatformCompat; import com.android.internal.content.PackageMonitor; import com.android.internal.infra.AndroidFuture; import com.android.internal.inputmethod.DirectBootAwareness; @@ -279,6 +277,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub final WindowManagerInternal mWindowManagerInternal; final PackageManagerInternal mPackageManagerInternal; final InputManagerInternal mInputManagerInternal; + final ImePlatformCompatUtils mImePlatformCompatUtils; final boolean mHasFeature; private final ArrayMap<String, List<InputMethodSubtype>> mAdditionalSubtypeMap = new ArrayMap<>(); @@ -691,8 +690,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub */ boolean mIsInteractive = true; - private final IPlatformCompat mPlatformCompat; - int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT; /** @@ -1627,6 +1624,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); + mImePlatformCompatUtils = new ImePlatformCompatUtils(); mImeDisplayValidator = mWindowManagerInternal::getDisplayImePolicy; mAppOpsManager = mContext.getSystemService(AppOpsManager.class); mUserManager = mContext.getSystemService(UserManager.class); @@ -1634,8 +1632,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mAccessibilityManager = AccessibilityManager.getInstance(context); mHasFeature = context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_INPUT_METHODS); - mPlatformCompat = IPlatformCompat.Stub.asInterface( - ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); + mSlotIme = mContext.getString(com.android.internal.R.string.status_bar_ime); Bundle extras = new Bundle(); @@ -3620,6 +3617,14 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub return InputBindResult.INVALID_USER; } + final boolean shouldClearFlag = mImePlatformCompatUtils.shouldClearShowForcedFlag(cs.uid); + // In case mShowForced flag affects the next client to keep IME visible, when the current + // client is leaving due to the next focused client, we clear mShowForced flag when the + // next client's targetSdkVersion is T or higher. + if (mCurFocusedWindow != windowToken && mShowForced && shouldClearFlag) { + mShowForced = false; + } + // cross-profile access is always allowed here to allow profile-switching. if (!mSettings.isCurrentProfile(userId)) { Slog.w(TAG, "A background user is requesting window. Hiding IME."); @@ -4728,14 +4733,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // Inform the current client of the change in active status if (mCurClient != null && mCurClient.client != null) { - boolean reportToImeController = false; - try { - reportToImeController = mPlatformCompat.isChangeEnabledByUid( - FINISH_INPUT_NO_FALLBACK_CONNECTION, getCurMethodUidLocked()); - } catch (RemoteException e) { - } scheduleSetActiveToClient(mCurClient, mIsInteractive, mInFullscreenMode, - reportToImeController); + mImePlatformCompatUtils.shouldFinishInputWithReportToIme( + getCurMethodUidLocked())); } } } diff --git a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java index 01aee7bc1942..db81393a9ad6 100644 --- a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java +++ b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java @@ -34,7 +34,6 @@ import android.content.pm.PackageManagerInternal; import android.os.Binder; import android.os.HandlerThread; import android.os.LocaleList; -import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.text.TextUtils; @@ -45,7 +44,6 @@ import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.content.PackageMonitor; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; @@ -89,32 +87,24 @@ class LocaleManagerBackupHelper { // SparseArray because it is more memory-efficient than a HashMap. private final SparseArray<StagedData> mStagedData; - private final PackageMonitor mPackageMonitor; private final BroadcastReceiver mUserMonitor; LocaleManagerBackupHelper(LocaleManagerService localeManagerService, - PackageManagerInternal pmInternal) { + PackageManagerInternal pmInternal, HandlerThread broadcastHandlerThread) { this(localeManagerService.mContext, localeManagerService, pmInternal, Clock.systemUTC(), - new SparseArray<>()); + new SparseArray<>(), broadcastHandlerThread); } @VisibleForTesting LocaleManagerBackupHelper(Context context, LocaleManagerService localeManagerService, - PackageManagerInternal pmInternal, Clock clock, SparseArray<StagedData> stagedData) { + PackageManagerInternal pmInternal, Clock clock, SparseArray<StagedData> stagedData, + HandlerThread broadcastHandlerThread) { mContext = context; mLocaleManagerService = localeManagerService; mPackageManagerInternal = pmInternal; mClock = clock; mStagedData = stagedData; - HandlerThread broadcastHandlerThread = new HandlerThread(TAG, - Process.THREAD_PRIORITY_BACKGROUND); - broadcastHandlerThread.start(); - - mPackageMonitor = new PackageMonitorImpl(); - mPackageMonitor.register(context, broadcastHandlerThread.getLooper(), - UserHandle.ALL, - true); mUserMonitor = new UserMonitor(); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_REMOVED); @@ -127,11 +117,6 @@ class LocaleManagerBackupHelper { return mUserMonitor; } - @VisibleForTesting - PackageMonitor getPackageMonitor() { - return mPackageMonitor; - } - /** * @see LocaleManagerInternal#getBackupPayload(int userId) */ @@ -267,6 +252,53 @@ class LocaleManagerBackupHelper { BackupManager.dataChanged(SYSTEM_BACKUP_PACKAGE_KEY); } + /** + * <p><b>Note:</b> This is invoked by service's common monitor + * {@link LocaleManagerServicePackageMonitor#onPackageAdded} when a new package is + * added on device. + */ + void onPackageAdded(String packageName, int uid) { + try { + synchronized (mStagedDataLock) { + cleanStagedDataForOldEntriesLocked(); + + int userId = UserHandle.getUserId(uid); + if (mStagedData.contains(userId)) { + // Perform lazy restore only if the staged data exists. + doLazyRestoreLocked(packageName, userId); + } + } + } catch (Exception e) { + Slog.e(TAG, "Exception in onPackageAdded.", e); + } + } + + /** + * <p><b>Note:</b> This is invoked by service's common monitor + * {@link LocaleManagerServicePackageMonitor#onPackageDataCleared} when a package's data + * is cleared. + */ + void onPackageDataCleared() { + try { + notifyBackupManager(); + } catch (Exception e) { + Slog.e(TAG, "Exception in onPackageDataCleared.", e); + } + } + + /** + * <p><b>Note:</b> This is invoked by service's common monitor + * {@link LocaleManagerServicePackageMonitor#onPackageRemoved} when a package is removed + * from device. + */ + void onPackageRemoved() { + try { + notifyBackupManager(); + } catch (Exception e) { + Slog.e(TAG, "Exception in onPackageRemoved.", e); + } + } + private boolean isPackageInstalledForUser(String packageName, int userId) { PackageInfo pkgInfo = null; try { @@ -395,48 +427,6 @@ class LocaleManagerBackupHelper { } /** - * Helper to monitor package states. - * - * <p>We're interested in package added, package data cleared and package removed events. - */ - private final class PackageMonitorImpl extends PackageMonitor { - @Override - public void onPackageAdded(String packageName, int uid) { - try { - synchronized (mStagedDataLock) { - cleanStagedDataForOldEntriesLocked(); - - int userId = UserHandle.getUserId(uid); - if (mStagedData.contains(userId)) { - // Perform lazy restore only if the staged data exists. - doLazyRestoreLocked(packageName, userId); - } - } - } catch (Exception e) { - Slog.e(TAG, "Exception in onPackageAdded.", e); - } - } - - @Override - public void onPackageDataCleared(String packageName, int uid) { - try { - notifyBackupManager(); - } catch (Exception e) { - Slog.e(TAG, "Exception in onPackageDataCleared.", e); - } - } - - @Override - public void onPackageRemoved(String packageName, int uid) { - try { - notifyBackupManager(); - } catch (Exception e) { - Slog.e(TAG, "Exception in onPackageRemoved.", e); - } - } - } - - /** * Performs lazy restore from the staged data. * * <p>This is invoked by the package monitor on the package added callback. diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java index d459f8df99b6..c42770555bab 100644 --- a/services/core/java/com/android/server/locales/LocaleManagerService.java +++ b/services/core/java/com/android/server/locales/LocaleManagerService.java @@ -29,6 +29,7 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.os.Binder; +import android.os.HandlerThread; import android.os.LocaleList; import android.os.Process; import android.os.RemoteException; @@ -38,14 +39,13 @@ import android.os.UserHandle; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.DumpUtils; +import com.android.internal.content.PackageMonitor; import com.android.internal.util.FrameworkStatsLog; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.wm.ActivityTaskManagerInternal; import java.io.FileDescriptor; -import java.io.PrintWriter; /** * The implementation of ILocaleManager.aidl. @@ -62,6 +62,8 @@ public class LocaleManagerService extends SystemService { private LocaleManagerBackupHelper mBackupHelper; + private final PackageMonitor mPackageMonitor; + public static final boolean DEBUG = false; public LocaleManagerService(Context context) { @@ -71,15 +73,26 @@ public class LocaleManagerService extends SystemService { mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class); mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); + + HandlerThread broadcastHandlerThread = new HandlerThread(TAG, + Process.THREAD_PRIORITY_BACKGROUND); + broadcastHandlerThread.start(); + mBackupHelper = new LocaleManagerBackupHelper(this, - mPackageManagerInternal); + mPackageManagerInternal, broadcastHandlerThread); + + mPackageMonitor = new LocaleManagerServicePackageMonitor(mBackupHelper); + mPackageMonitor.register(context, broadcastHandlerThread.getLooper(), + UserHandle.ALL, + true); } @VisibleForTesting LocaleManagerService(Context context, ActivityTaskManagerInternal activityTaskManagerInternal, ActivityManagerInternal activityManagerInternal, PackageManagerInternal packageManagerInternal, - LocaleManagerBackupHelper localeManagerBackupHelper) { + LocaleManagerBackupHelper localeManagerBackupHelper, + PackageMonitor packageMonitor) { super(context); mContext = context; mBinderService = new LocaleManagerBinderService(); @@ -87,6 +100,7 @@ public class LocaleManagerService extends SystemService { mActivityManagerInternal = activityManagerInternal; mPackageManagerInternal = packageManagerInternal; mBackupHelper = localeManagerBackupHelper; + mPackageMonitor = packageMonitor; } @Override @@ -130,11 +144,6 @@ public class LocaleManagerService extends SystemService { } @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - LocaleManagerService.this.dump(fd, pw, args); - } - - @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver) { @@ -407,14 +416,6 @@ public class LocaleManagerService extends SystemService { return null; } - /** - * Dumps useful info related to service. - */ - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; - // TODO(b/201766221): Implement when there is state. - } - private void logMetric(@NonNull AppLocaleChangedAtomRecord atomRecordForMetrics) { FrameworkStatsLog.write(FrameworkStatsLog.APPLICATION_LOCALES_CHANGED, atomRecordForMetrics.mCallingUid, diff --git a/services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java b/services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java new file mode 100644 index 000000000000..b459be768b9f --- /dev/null +++ b/services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.locales; + +import com.android.internal.content.PackageMonitor; + +/** + * Helper to monitor package states inside {@link LocaleManagerService}. + * + * <p> These listeners forward the call to different aspects of locale service that + * handle the business logic. + * <p> We're interested in package added, package data cleared and package removed events. + */ +final class LocaleManagerServicePackageMonitor extends PackageMonitor { + private LocaleManagerBackupHelper mBackupHelper; + + LocaleManagerServicePackageMonitor(LocaleManagerBackupHelper localeManagerBackupHelper) { + mBackupHelper = localeManagerBackupHelper; + } + + @Override + public void onPackageAdded(String packageName, int uid) { + mBackupHelper.onPackageAdded(packageName, uid); + } + + @Override + public void onPackageDataCleared(String packageName, int uid) { + mBackupHelper.onPackageDataCleared(); + } + + @Override + public void onPackageRemoved(String packageName, int uid) { + mBackupHelper.onPackageRemoved(); + } +} diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java index d42e2c63e80d..0b8f94c574c6 100644 --- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java @@ -1607,6 +1607,8 @@ public class LocationProviderManager extends public @Nullable Location getLastLocation(LastLocationRequest request, CallerIdentity identity, @PermissionLevel int permissionLevel) { + request = calculateLastLocationRequest(request); + if (!isActive(request.isBypass(), identity)) { return null; } @@ -1634,6 +1636,38 @@ public class LocationProviderManager extends return location; } + private LastLocationRequest calculateLastLocationRequest(LastLocationRequest baseRequest) { + LastLocationRequest.Builder builder = new LastLocationRequest.Builder(baseRequest); + + boolean locationSettingsIgnored = baseRequest.isLocationSettingsIgnored(); + if (locationSettingsIgnored) { + // if we are not currently allowed use location settings ignored, disable it + if (!mSettingsHelper.getIgnoreSettingsAllowlist().contains( + getIdentity().getPackageName(), getIdentity().getAttributionTag()) + && !mLocationManagerInternal.isProvider(null, getIdentity())) { + locationSettingsIgnored = false; + } + + builder.setLocationSettingsIgnored(locationSettingsIgnored); + } + + boolean adasGnssBypass = baseRequest.isAdasGnssBypass(); + if (adasGnssBypass) { + // if we are not currently allowed use adas gnss bypass, disable it + if (!GPS_PROVIDER.equals(mName)) { + Log.e(TAG, "adas gnss bypass request received in non-gps provider"); + adasGnssBypass = false; + } else if (!mLocationSettings.getUserSettings( + getIdentity().getUserId()).isAdasGnssLocationEnabled()) { + adasGnssBypass = false; + } + + builder.setAdasGnssBypass(adasGnssBypass); + } + + return builder.build(); + } + /** * This function does not perform any permissions or safety checks, by calling it you are * committing to performing all applicable checks yourself. This always returns a "fine" diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index c09c904ff931..db0b0c58b046 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -183,7 +183,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.concurrent.ExecutorService; @@ -1596,18 +1595,16 @@ final class InstallPackageHelper { parsedPackage.setRestrictUpdateHash(oldPackage.getRestrictUpdateHash()); } - // Check for shared user id changes - if (!Objects.equals(oldPackage.getSharedUserId(), - parsedPackage.getSharedUserId()) - // Don't mark as invalid if the app is trying to - // leave a sharedUserId - && parsedPackage.getSharedUserId() != null) { + // APK should not change its sharedUserId declarations + final var oldSharedUid = oldPackage.getSharedUserId() != null + ? oldPackage.getSharedUserId() : "<nothing>"; + final var newSharedUid = parsedPackage.getSharedUserId() != null + ? parsedPackage.getSharedUserId() : "<nothing>"; + if (!oldSharedUid.equals(newSharedUid)) { throw new PrepareFailure(INSTALL_FAILED_UID_CHANGED, "Package " + parsedPackage.getPackageName() + " shared user changed from " - + (oldPackage.getSharedUserId() != null - ? oldPackage.getSharedUserId() : "<nothing>") - + " to " + parsedPackage.getSharedUserId()); + + oldSharedUid + " to " + newSharedUid); } // In case of rollback, remember per-user/profile install state @@ -3696,10 +3693,13 @@ final class InstallPackageHelper { } disabledPkgSetting = mPm.mSettings.getDisabledSystemPkgLPr( parsedPackage.getPackageName()); - sharedUserSetting = (parsedPackage.getSharedUserId() != null) - ? mPm.mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(), - 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true) - : null; + if (parsedPackage.getSharedUserId() != null && !parsedPackage.isLeavingSharedUid()) { + sharedUserSetting = mPm.mSettings.getSharedUserLPw( + parsedPackage.getSharedUserId(), + 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/); + } else { + sharedUserSetting = null; + } if (DEBUG_PACKAGE_SCANNING && (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0 && sharedUserSetting != null) { diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java index cdc2b1245b1e..f6f9faf98c40 100644 --- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java +++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java @@ -290,6 +290,9 @@ public interface ParsingPackage extends ParsingPackageRead { /** @see R#styleable.AndroidManifest_inheritKeyStoreKeys */ ParsingPackage setInheritKeyStoreKeys(boolean inheritKeyStoreKeys); + /** @see R#styleable.AndroidManifest_sharedUserMaxSdkVersion */ + ParsingPackage setLeavingSharedUid(boolean leavingSharedUid); + ParsingPackage setLabelRes(int labelRes); ParsingPackage setLargestWidthLimitDp(int largestWidthLimitDp); diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java index 177eaca8e06f..67670272ef8b 100644 --- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java +++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java @@ -549,6 +549,7 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden, private static final long SDK_LIBRARY = 1L << 49; private static final long INHERIT_KEYSTORE_KEYS = 1L << 50; private static final long ENABLE_ON_BACK_INVOKED_CALLBACK = 1L << 51; + private static final long LEAVING_SHARED_UID = 1L << 52; } private ParsingPackageImpl setBoolean(@Booleans.Values long flag, boolean value) { @@ -2403,6 +2404,11 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden, } @Override + public boolean isLeavingSharedUid() { + return getBoolean(Booleans.LEAVING_SHARED_UID); + } + + @Override public ParsingPackageImpl setBaseRevisionCode(int value) { baseRevisionCode = value; return this; @@ -2551,6 +2557,11 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden, } @Override + public ParsingPackageImpl setLeavingSharedUid(boolean value) { + return setBoolean(Booleans.LEAVING_SHARED_UID, value); + } + + @Override public ParsingPackageImpl setLabelRes(int value) { labelRes = value; return this; diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java index 428374fa21a8..50033f652bfd 100644 --- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java +++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java @@ -360,4 +360,11 @@ public interface ParsingPackageRead extends PkgWithoutStateAppInfo, PkgWithoutSt * @see R.styleable.AndroidManifestApplication_enableOnBackInvokedCallback */ boolean isOnBackInvokedCallbackEnabled(); + + /** + * Returns true if R.styleable#AndroidManifest_sharedUserMaxSdkVersion is set to a value + * smaller than the current SDK version. + * @see R.styleable#AndroidManifest_sharedUserMaxSdkVersion + */ + boolean isLeavingSharedUid(); } diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java index f30daa930e6c..ed1ab01e1d12 100644 --- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java +++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java @@ -1032,11 +1032,6 @@ public class ParsingPackageUtils { private static ParseResult<ParsingPackage> parseSharedUser(ParseInput input, ParsingPackage pkg, TypedArray sa) { - int maxSdkVersion = anInteger(0, R.styleable.AndroidManifest_sharedUserMaxSdkVersion, sa); - if ((maxSdkVersion != 0) && maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT) { - return input.success(pkg); - } - String str = nonConfigString(0, R.styleable.AndroidManifest_sharedUserId, sa); if (TextUtils.isEmpty(str)) { return input.success(pkg); @@ -1052,7 +1047,11 @@ public class ParsingPackageUtils { } } + int maxSdkVersion = anInteger(0, R.styleable.AndroidManifest_sharedUserMaxSdkVersion, sa); + boolean leaving = (maxSdkVersion != 0) && (maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT); + return input.success(pkg + .setLeavingSharedUid(leaving) .setSharedUserId(str.intern()) .setSharedUserLabel(resId(R.styleable.AndroidManifest_sharedUserLabel, sa))); } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 4c5c7054e9a0..fd24565798fb 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -5499,7 +5499,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * display, which set unrestricted keep-clear areas. * * For context on restricted vs unrestricted keep-clear areas, see - * {@link android.Manifest.permission.USE_UNRESTRICTED_KEEP_CLEAR_AREAS}. + * {@link android.Manifest.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS}. */ void getKeepClearAreas(List<Rect> outRestricted, List<Rect> outUnrestricted) { final Matrix tmpMatrix = new Matrix(); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index ffa1a601d41b..98c74f8ad81d 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -5149,10 +5149,13 @@ class Task extends TaskFragment { // Ensure that we do not trigger entering PiP an activity on the root pinned task return false; } + final boolean isTransient = opts != null && opts.getTransientLaunch(); final Task targetRootTask = toFrontTask != null ? toFrontTask.getRootTask() : toFrontActivity.getRootTask(); - if (targetRootTask != null && targetRootTask.isActivityTypeAssistant()) { - // Ensure the task/activity being brought forward is not the assistant + if (targetRootTask != null && (targetRootTask.isActivityTypeAssistant() || isTransient)) { + // Ensure the task/activity being brought forward is not the assistant and is not + // transient. In the case of transient-launch, we want to wait until the end of the + // transition and only allow switch if the transient launch was committed. return false; } return true; diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 0b965c37a2ba..3a3103e752ad 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -459,9 +459,19 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe // activity in a bad state. if (!visibleAtTransitionEnd && !ar.isVisibleRequested()) { boolean commitVisibility = true; - if (ar.getDeferHidingClient() && ar.getTask() != null) { + if (ar.isVisible() && ar.getTask() != null) { if (ar.pictureInPictureArgs != null && ar.pictureInPictureArgs.isAutoEnterEnabled()) { + if (mTransientLaunches != null) { + for (int j = 0; j < mTransientLaunches.size(); ++j) { + if (mTransientLaunches.valueAt(j).isVisibleRequested()) { + // force enable pip-on-task-switch now that we've committed + // to actually launching to the transient activity. + ar.supportsEnterPipOnTaskSwitch = true; + break; + } + } + } mController.mAtm.enterPictureInPictureMode(ar, ar.pictureInPictureArgs); // Avoid commit visibility to false here, or else we will get a sudden // "flash" / surface going invisible for a split second. diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index c267cbacdf24..c13ae95217b5 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -30,6 +30,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.IApplicationThread; +import android.app.WindowConfiguration; import android.os.IBinder; import android.os.IRemoteCallback; import android.os.RemoteException; @@ -296,6 +297,17 @@ class TransitionController { return ci.mVisible; } + @WindowConfiguration.WindowingMode + int getWindowingModeAtStart(@NonNull WindowContainer wc) { + if (mCollectingTransition == null) return wc.getWindowingMode(); + final Transition.ChangeInfo ci = mCollectingTransition.mChanges.get(wc); + if (ci == null) { + // not part of transition, so use current state. + return wc.getWindowingMode(); + } + return ci.mWindowingMode; + } + @WindowManager.TransitionType int getCollectingTransitionType() { return mCollectingTransition != null ? mCollectingTransition.mType : TRANSIT_NONE; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 5b1021eefc0a..709f885db776 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -200,6 +200,7 @@ import android.os.PowerManager.ServiceType; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.RemoteCallback; +import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; @@ -288,6 +289,7 @@ import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.IResultReceiver; import com.android.internal.policy.IKeyguardDismissCallback; +import com.android.internal.policy.IKeyguardLockedStateListener; import com.android.internal.policy.IShortcutService; import com.android.internal.policy.KeyInterceptionInfo; import com.android.internal.protolog.ProtoLogImpl; @@ -460,6 +462,10 @@ public class WindowManagerService extends IWindowManager.Stub final private KeyguardDisableHandler mKeyguardDisableHandler; + private final RemoteCallbackList<IKeyguardLockedStateListener> mKeyguardLockedStateListeners = + new RemoteCallbackList<>(); + private boolean mDispatchedKeyguardLockedState = false; + // VR Vr2d Display Id. int mVr2dDisplayId = INVALID_DISPLAY; boolean mVrModeEnabled = false; @@ -3029,6 +3035,7 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void onKeyguardShowingAndNotOccludedChanged() { mH.sendEmptyMessage(H.RECOMPUTE_FOCUS); + dispatchKeyguardLockedStateState(); } @Override @@ -3218,6 +3225,50 @@ public class WindowManagerService extends IWindowManager.Stub } } + @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) + @Override + public void addKeyguardLockedStateListener(IKeyguardLockedStateListener listener) { + enforceSubscribeToKeyguardLockedStatePermission(); + boolean registered = mKeyguardLockedStateListeners.register(listener); + if (!registered) { + Slog.w(TAG, "Failed to register listener: " + listener); + } + } + + @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) + @Override + public void removeKeyguardLockedStateListener(IKeyguardLockedStateListener listener) { + enforceSubscribeToKeyguardLockedStatePermission(); + mKeyguardLockedStateListeners.unregister(listener); + } + + private void enforceSubscribeToKeyguardLockedStatePermission() { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE, + Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE + + " permission required to read keyguard visibility"); + } + + private void dispatchKeyguardLockedStateState() { + mH.post(() -> { + final boolean isKeyguardLocked = mPolicy.isKeyguardShowing(); + if (mDispatchedKeyguardLockedState == isKeyguardLocked) { + return; + } + final int n = mKeyguardLockedStateListeners.beginBroadcast(); + for (int i = 0; i < n; i++) { + try { + mKeyguardLockedStateListeners.getBroadcastItem(i).onKeyguardLockedStateChanged( + isKeyguardLocked); + } catch (RemoteException e) { + // Handled by the RemoteCallbackList. + } + } + mKeyguardLockedStateListeners.finishBroadcast(); + mDispatchedKeyguardLockedState = isKeyguardLocked; + }); + } + @Override public void setSwitchingUser(boolean switching) { if (!checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index ccaa03ae8fd0..a2e8813c998a 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; @@ -645,8 +646,10 @@ class WindowToken extends WindowContainer<WindowState> { final ActivityRecord r = asActivityRecord(); if (r != null) { final Task rootTask = r.getRootTask(); - // Don't transform the activity in PiP because the PiP task organizer will handle it. - if (rootTask != null && rootTask.inPinnedWindowingMode()) { + // Don't transform the activity exiting PiP because the PiP task organizer will handle + // it. + if (rootTask != null && mTransitionController.getWindowingModeAtStart(rootTask) + == WINDOWING_MODE_PINNED) { return; } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index e895d377f607..5098abe4bb55 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -386,8 +386,8 @@ public final class SystemServer implements Dumpable { "com.android.server.DeviceIdleController"; private static final String BLOB_STORE_MANAGER_SERVICE_CLASS = "com.android.server.blob.BlobStoreManagerService"; - private static final String APP_SEARCH_MANAGER_SERVICE_CLASS = - "com.android.server.appsearch.AppSearchManagerService"; + private static final String APPSEARCH_MODULE_LIFECYCLE_CLASS = + "com.android.server.appsearch.AppSearchModule$Lifecycle"; private static final String ISOLATED_COMPILATION_SERVICE_CLASS = "com.android.server.compos.IsolatedCompilationService"; private static final String ROLLBACK_MANAGER_SERVICE_CLASS = @@ -2764,8 +2764,8 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(SAFETY_CENTER_SERVICE_CLASS); t.traceEnd(); - t.traceBegin("AppSearchManagerService"); - mSystemServiceManager.startService(APP_SEARCH_MANAGER_SERVICE_CLASS); + t.traceBegin("AppSearchModule"); + mSystemServiceManager.startService(APPSEARCH_MODULE_LIFECYCLE_CLASS); t.traceEnd(); if (SystemProperties.getBoolean("ro.config.isolated_compilation_enabled", false)) { diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt index cd2d0fce6691..83ccabf03935 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt @@ -238,6 +238,7 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag AndroidPackage::isVendor, AndroidPackage::isVisibleToInstantApps, AndroidPackage::isVmSafeMode, + AndroidPackage::isLeavingSharedUid, AndroidPackage::isResetEnabledSettingsOnAppDataCleared, AndroidPackage::getMaxAspectRatio, AndroidPackage::getMinAspectRatio, diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java index d2358a08624d..023608c68ee3 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java @@ -1167,7 +1167,14 @@ public class GameManagerServiceTests { startUser(gameManagerService, USER_ID_1); gameManagerService.setGameMode( mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1); - GameState gameState = new GameState(isLoading, GameState.MODE_NONE); + int testMode = GameState.MODE_NONE; + int testLabel = 99; + int testQuality = 123; + GameState gameState = new GameState(isLoading, testMode, testLabel, testQuality); + assertEquals(isLoading, gameState.isLoading()); + assertEquals(testMode, gameState.getMode()); + assertEquals(testLabel, gameState.getLabel()); + assertEquals(testQuality, gameState.getQuality()); gameManagerService.setGameState(mPackageName, gameState, USER_ID_1); mTestLooper.dispatchAll(); verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME_LOADING, isLoading); diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java index 890a5495ef16..4e4854c6688d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java @@ -333,6 +333,10 @@ public class LocationProviderManagerTest { @Test public void testGetLastLocation_Bypass() { + mInjector.getSettingsHelper().setIgnoreSettingsAllowlist( + new PackageTagsList.Builder().add( + IDENTITY.getPackageName()).build()); + assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY, PERMISSION_FINE)).isNull(); assertThat(mManager.getLastLocation( @@ -381,6 +385,14 @@ public class LocationProviderManagerTest { new LastLocationRequest.Builder().setLocationSettingsIgnored(true).build(), IDENTITY, PERMISSION_FINE)).isEqualTo( loc); + + mInjector.getSettingsHelper().setIgnoreSettingsAllowlist( + new PackageTagsList.Builder().build()); + mProvider.setProviderAllowed(false); + + assertThat(mManager.getLastLocation( + new LastLocationRequest.Builder().setLocationSettingsIgnored(true).build(), + IDENTITY, PERMISSION_FINE)).isNull(); } @Test diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java index c77100045118..0287510041be 100644 --- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java +++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java @@ -41,7 +41,9 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.os.Binder; +import android.os.HandlerThread; import android.os.LocaleList; +import android.os.Process; import android.os.RemoteException; import android.os.SimpleClock; import android.util.SparseArray; @@ -78,6 +80,7 @@ import java.util.Map; */ @RunWith(AndroidJUnit4.class) public class LocaleManagerBackupRestoreTest { + private static final String TAG = "LocaleManagerBackupRestoreTest"; private static final String DEFAULT_PACKAGE_NAME = "com.android.myapp"; private static final String DEFAULT_LOCALE_TAGS = "en-XC,ar-XB"; private static final String TEST_LOCALES_XML_TAG = "locales"; @@ -131,12 +134,17 @@ public class LocaleManagerBackupRestoreTest { doReturn(mMockPackageManager).when(mMockContext).getPackageManager(); + HandlerThread broadcastHandlerThread = new HandlerThread(TAG, + Process.THREAD_PRIORITY_BACKGROUND); + broadcastHandlerThread.start(); + mBackupHelper = spy(new ShadowLocaleManagerBackupHelper(mMockContext, - mMockLocaleManagerService, mMockPackageManagerInternal, mClock, STAGE_DATA)); + mMockLocaleManagerService, mMockPackageManagerInternal, mClock, STAGE_DATA, + broadcastHandlerThread)); doNothing().when(mBackupHelper).notifyBackupManager(); mUserMonitor = mBackupHelper.getUserMonitor(); - mPackageMonitor = mBackupHelper.getPackageMonitor(); + mPackageMonitor = new LocaleManagerServicePackageMonitor(mBackupHelper); setCurrentTimeMillis(DEFAULT_CREATION_TIME_MILLIS); } diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java index ca5b0cb1112f..0b3ef4542cbf 100644 --- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java @@ -46,6 +46,7 @@ import android.os.LocaleList; import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.android.internal.content.PackageMonitor; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal.PackageConfig; @@ -86,6 +87,8 @@ public class LocaleManagerServiceTest { private ActivityTaskManagerInternal mMockActivityTaskManager; @Mock private ActivityManagerInternal mMockActivityManager; + @Mock + PackageMonitor mMockPackageMonitor; @Before public void setUp() throws Exception { @@ -93,6 +96,7 @@ public class LocaleManagerServiceTest { mMockActivityTaskManager = mock(ActivityTaskManagerInternal.class); mMockActivityManager = mock(ActivityManagerInternal.class); mMockPackageManagerInternal = mock(PackageManagerInternal.class); + mMockPackageMonitor = mock(PackageMonitor.class); // For unit tests, set the default installer info PackageManager mockPackageManager = mock(PackageManager.class); @@ -113,7 +117,8 @@ public class LocaleManagerServiceTest { mMockBackupHelper = mock(ShadowLocaleManagerBackupHelper.class); mLocaleManagerService = new LocaleManagerService(mMockContext, mMockActivityTaskManager, - mMockActivityManager, mMockPackageManagerInternal, mMockBackupHelper); + mMockActivityManager, mMockPackageManagerInternal, + mMockBackupHelper, mMockPackageMonitor); } @Test(expected = SecurityException.class) diff --git a/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java b/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java index b0fc6363b701..ad9be0d5fe8d 100644 --- a/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java +++ b/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java @@ -18,6 +18,7 @@ package com.android.server.locales; import android.content.Context; import android.content.pm.PackageManagerInternal; +import android.os.HandlerThread; import android.util.SparseArray; import java.time.Clock; @@ -31,7 +32,8 @@ public class ShadowLocaleManagerBackupHelper extends LocaleManagerBackupHelper { ShadowLocaleManagerBackupHelper(Context context, LocaleManagerService localeManagerService, PackageManagerInternal pmInternal, Clock clock, - SparseArray<LocaleManagerBackupHelper.StagedData> stagedData) { - super(context, localeManagerService, pmInternal, clock, stagedData); + SparseArray<LocaleManagerBackupHelper.StagedData> stagedData, + HandlerThread broadcastHandlerThread) { + super(context, localeManagerService, pmInternal, clock, stagedData, broadcastHandlerThread); } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt index 8d60466eff95..4cddd8506df7 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt @@ -60,7 +60,7 @@ abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter) } teardown { test { - testApp.exit() + testApp.exit(wmHelper) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt index 7ee6451b2797..5bd365c7eefd 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt @@ -64,7 +64,6 @@ open class ImeAppHelper @JvmOverloads constructor( device.waitForIdle() } else { wmHelper.waitImeShown() - wmHelper.waitForAppTransitionIdle() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt index b66c45c7c9f0..a135e0af067b 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt @@ -53,8 +53,8 @@ class TwoActivitiesAppHelper @JvmOverloads constructor( button.click() device.wait(Until.gone(launchActivityButton), FIND_TIMEOUT) - wmHelper.waitForFullScreenApp(secondActivityComponent) wmHelper.waitFor( + WindowManagerStateHelper.isAppFullScreen(secondActivityComponent), WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY), WindowManagerConditionsFactory.hasLayersAnimating().negate() ) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt index ba5698cafa15..a9564fdd2332 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt @@ -88,7 +88,7 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) { } transitions { device.reopenAppFromOverview(wmHelper) - require(wmHelper.waitImeShown()) { "IME didn't show in time" } + wmHelper.waitImeShown() } teardown { test { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt index 19e2c92304d4..7e3ed8252f4c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt @@ -18,6 +18,7 @@ package com.android.server.wm.flicker.ime import android.app.Instrumentation import android.platform.test.annotations.Presubmit +import android.view.Display import android.view.Surface import android.view.WindowManagerPolicyConstants import androidx.test.filters.RequiresDevice @@ -35,6 +36,8 @@ import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.navBarWindowIsVisible import com.android.server.wm.flicker.statusBarWindowIsVisible import com.android.server.wm.traces.common.FlickerComponentName +import com.android.server.wm.traces.common.WindowManagerConditionsFactory +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import org.junit.FixMethodOrder import org.junit.Test @@ -64,12 +67,22 @@ class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParame eachRun { this.setRotation(testSpec.startRotation) testApp.launchViaIntent(wmHelper) - wmHelper.waitForFullScreenApp(testApp.component) - wmHelper.waitForAppTransitionIdle() + val testAppVisible = wmHelper.waitFor( + WindowManagerStateHelper.isAppFullScreen(testApp.component), + WindowManagerConditionsFactory.isAppTransitionIdle( + Display.DEFAULT_DISPLAY)) + require(testAppVisible) { + "Expected ${testApp.component.toWindowName()} to be visible" + } imeTestApp.launchViaIntent(wmHelper) - wmHelper.waitForFullScreenApp(testApp.component) - wmHelper.waitForAppTransitionIdle() + val imeAppVisible = wmHelper.waitFor( + WindowManagerStateHelper.isAppFullScreen(imeTestApp.component), + WindowManagerConditionsFactory.isAppTransitionIdle( + Display.DEFAULT_DISPLAY)) + require(imeAppVisible) { + "Expected ${imeTestApp.component.toWindowName()} to be visible" + } imeTestApp.openIME(device, wmHelper) } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt index b5e13be7dca0..cc808a0ce871 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt @@ -18,6 +18,7 @@ package com.android.server.wm.flicker.launch import android.app.Instrumentation import android.platform.test.annotations.Presubmit +import android.view.Display import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.entireScreenCovered @@ -30,7 +31,9 @@ import com.android.server.wm.flicker.annotation.Group4 import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.TwoActivitiesAppHelper import com.android.server.wm.flicker.testapp.ActivityOptions +import com.android.server.wm.traces.common.WindowManagerConditionsFactory import com.android.server.wm.traces.parser.toFlickerComponent +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -77,14 +80,16 @@ class ActivitiesTransitionTest(val testSpec: FlickerTestParameter) { } teardown { test { - testApp.exit() + testApp.exit(wmHelper) } } transitions { testApp.openSecondActivity(device, wmHelper) device.pressBack() - wmHelper.waitForAppTransitionIdle() - wmHelper.waitForFullScreenApp(testApp.component) + val firstActivityVisible = wmHelper.waitFor( + WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY), + WindowManagerStateHelper.isAppFullScreen(testApp.component)) + require(firstActivityVisible) { "Expected ${testApp.component} to be visible" } } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt index 53560cc14c44..4313b8dbc883 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt @@ -56,7 +56,7 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) { } teardown { test { - testApp.exit() + testApp.exit(wmHelper) } } } |