diff options
51 files changed, 1070 insertions, 599 deletions
diff --git a/Android.bp b/Android.bp index bc6cda38e0c8..cff863b44499 100644 --- a/Android.bp +++ b/Android.bp @@ -343,6 +343,7 @@ java_defaults { "hardware/interfaces/biometrics/fingerprint/aidl", "hardware/interfaces/graphics/common/aidl", "hardware/interfaces/keymaster/aidl", + "system/hardware/interfaces/media/aidl", ], }, dxflags: [ @@ -632,6 +633,7 @@ stubs_defaults { "hardware/interfaces/biometrics/fingerprint/aidl", "hardware/interfaces/graphics/common/aidl", "hardware/interfaces/keymaster/aidl", + "system/hardware/interfaces/media/aidl", ], }, // These are libs from framework-internal-utils that are required (i.e. being referenced) diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 28d4433f1e21..2751b54ebdb7 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -6657,12 +6657,13 @@ public final class ActivityThread extends ClientTransactionHandler // Setup a location to store generated/compiled graphics code. final Context deviceContext = context.createDeviceProtectedStorageContext(); final File codeCacheDir = deviceContext.getCodeCacheDir(); - if (codeCacheDir != null) { + final File deviceCacheDir = deviceContext.getCacheDir(); + if (codeCacheDir != null && deviceCacheDir != null) { try { int uid = Process.myUid(); String[] packages = getPackageManager().getPackagesForUid(uid); if (packages != null) { - HardwareRenderer.setupDiskCache(codeCacheDir); + HardwareRenderer.setupDiskCache(deviceCacheDir); RenderScriptCacheDir.setupDiskCache(codeCacheDir); } } catch (RemoteException e) { diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index 0ef8e922bf06..09450f59ed3d 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -155,6 +155,14 @@ public class KeyguardManager { "android.app.extra.REMOTE_LOCKSCREEN_VALIDATION_SESSION"; /** + * A boolean indicating that credential confirmation activity should be a task overlay. + * {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER}. + * @hide + */ + public static final String EXTRA_FORCE_TASK_OVERLAY = + "android.app.KeyguardManager.FORCE_TASK_OVERLAY"; + + /** * Result code returned by the activity started by * {@link #createConfirmFactoryResetCredentialIntent} or * {@link #createConfirmDeviceCredentialForRemoteValidationIntent} diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 440ee202cc5b..e78fb179eb6c 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -8657,13 +8657,13 @@ public class Notification implements Parcelable * where the platform doesn't support the MIME type, the original text provided in the * constructor will be used. * @param dataMimeType The MIME type of the content. See - * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME - * types on Android and Android Wear. + * {@link android.graphics.ImageDecoder#isMimeTypeSupported(String)} for a list of + * supported image MIME types. * @param dataUri The uri containing the content whose type is given by the MIME type. * <p class="note"> + * Notification Listeners including the System UI need permission to access the + * data the Uri points to. The recommended ways to do this are: * <ol> - * <li>Notification Listeners including the System UI need permission to access the - * data the Uri points to. The recommended ways to do this are:</li> * <li>Store the data in your own ContentProvider, making sure that other apps have * the correct permission to access your provider. The preferred mechanism for * providing access is to use per-URI permissions which are temporary and only diff --git a/core/java/android/credentials/CredentialDescription.java b/core/java/android/credentials/CredentialDescription.java index bf34c1cc5712..a23d7e402768 100644 --- a/core/java/android/credentials/CredentialDescription.java +++ b/core/java/android/credentials/CredentialDescription.java @@ -42,7 +42,7 @@ public final class CredentialDescription implements Parcelable { private final String mType; /** - * The flattened JSON string that will be matched with requests. + * Flattened semicolon separated keys of JSON values to match with requests. */ @NonNull private final String mFlattenedRequestString; @@ -57,7 +57,8 @@ public final class CredentialDescription implements Parcelable { * Constructs a {@link CredentialDescription}. * * @param type the type of the credential returned. - * @param flattenedRequestString flattened JSON string that will be matched with requests. + * @param flattenedRequestString flattened semicolon separated keys of JSON values + * to match with requests. * @param credentialEntries a list of {@link CredentialEntry}s that are to be shown on the * account selector if a credential matches with this description. * Each entry contains information to be displayed within an @@ -151,4 +152,29 @@ public final class CredentialDescription implements Parcelable { public List<CredentialEntry> getCredentialEntries() { return mCredentialEntries; } + + /** + * {@link CredentialDescription#mType} and + * {@link CredentialDescription#mFlattenedRequestString} are enough for hashing. Constructor + * enforces {@link CredentialEntry} to have the same type and + * {@link android.app.slice.Slice} contained by the entry can not be hashed. + */ + @Override + public int hashCode() { + return Objects.hash(mType, mFlattenedRequestString); + } + + /** + * {@link CredentialDescription#mType} and + * {@link CredentialDescription#mFlattenedRequestString} are enough for equality check. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof CredentialDescription)) { + return false; + } + CredentialDescription other = (CredentialDescription) obj; + return mType.equals(other.mType) + && mFlattenedRequestString.equals(other.mFlattenedRequestString); + } } diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 722dd08d1ee1..e6b306955ef0 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -125,6 +125,23 @@ public final class CameraManager { "camera.enable_landscape_to_portrait"; /** + * Enable physical camera availability callbacks when the logical camera is unavailable + * + * <p>Previously once a logical camera becomes unavailable, no {@link + * #onPhysicalCameraAvailable} or {@link #onPhysicalCameraUnavailable} will be called until + * the logical camera becomes available again. The results in the app opening the logical + * camera not able to receive physical camera availability change.</p> + * + * <p>With this change, the {@link #onPhysicalCameraAvailable} and {@link + * #onPhysicalCameraUnavailable} can still be called while the logical camera is unavailable. + * </p> + */ + @ChangeId + @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + private static final long ENABLE_PHYSICAL_CAMERA_CALLBACK_FOR_UNAVAILABLE_LOGICAL_CAMERA = + 244358506L; + + /** * @hide */ public CameraManager(Context context) { @@ -1194,6 +1211,14 @@ public final class CameraManager { } /** + * @hide + */ + public static boolean physicalCallbacksAreEnabledForUnavailableCamera() { + return CompatChanges.isChangeEnabled( + ENABLE_PHYSICAL_CAMERA_CALLBACK_FOR_UNAVAILABLE_LOGICAL_CAMERA); + } + + /** * A callback for camera devices becoming available or unavailable to open. * * <p>Cameras become available when they are no longer in use, or when a new @@ -1270,9 +1295,10 @@ public final class CameraManager { * to begin with, {@link #onPhysicalCameraUnavailable} may be invoked after * {@link #onCameraAvailable}.</p> * - * <p>Limitation: Opening a logical camera disables the {@link #onPhysicalCameraAvailable} - * and {@link #onPhysicalCameraUnavailable} callbacks for its physical cameras. For example, - * if app A opens the camera device:</p> + * <p>If {@link android.content.pm.ApplicationInfo#targetSdkVersion targetSdkVersion} + * < {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, opening a logical camera + * disables the {@link #onPhysicalCameraAvailable} and {@link #onPhysicalCameraUnavailable} + * callbacks for its physical cameras. For example, if app A opens the camera device:</p> * * <ul> * @@ -1284,6 +1310,33 @@ public final class CameraManager { * * </ul> * + * <p>If {@link android.content.pm.ApplicationInfo#targetSdkVersion targetSdkVersion} + * ≥ {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}:</p> + * + * <ul> + * + * <li>A physical camera status change will trigger {@link #onPhysicalCameraAvailable} + * or {@link #onPhysicalCameraUnavailable} even after the logical camera becomes + * unavailable. A {@link #onCameraUnavailable} call for a logical camera doesn't reset the + * physical cameras' availability status. This makes it possible for an application opening + * the logical camera device to know which physical camera becomes unavailable or available + * to use.</li> + * + * <li>Similar to {@link android.os.Build.VERSION_CODES#TIRAMISU Android 13} and earlier, + * the logical camera's {@link #onCameraAvailable} callback implies all of its physical + * cameras' status become available. {@link #onPhysicalCameraUnavailable} will be called + * for any unavailable physical cameras upon the logical camera becoming available.</li> + * + * </ul> + * + * <p>Given the pipeline nature of the camera capture through {@link + * android.hardware.camera2.CaptureRequest}, there may be frame drops if the application + * requests images from a physical camera of a logical multi-camera and that physical camera + * becomes unavailable. The application should stop requesting directly from an unavailable + * physical camera as soon as {@link #onPhysicalCameraUnavailable} is received, and also be + * ready to robustly handle frame drop errors for requests targeting physical cameras, + * since those errors may arrive before the unavailability callback.</p> + * * <p>The default implementation of this method does nothing.</p> * * @param cameraId The unique identifier of the logical multi-camera. @@ -1306,9 +1359,10 @@ public final class CameraManager { * cameras of its parent logical multi-camera, when {@link #onCameraUnavailable} for * the logical multi-camera is invoked.</p> * - * <p>Limitation: Opening a logical camera disables the {@link #onPhysicalCameraAvailable} - * and {@link #onPhysicalCameraUnavailable} callbacks for its physical cameras. For example, - * if app A opens the camera device:</p> + * <p>If {@link android.content.pm.ApplicationInfo#targetSdkVersion targetSdkVersion} + * < {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, opening a logical camera + * disables the {@link #onPhysicalCameraAvailable} and {@link #onPhysicalCameraUnavailable} + * callbacks for its physical cameras. For example, if app A opens the camera device:</p> * * <ul> * @@ -1320,6 +1374,33 @@ public final class CameraManager { * * </ul> * + * <p>If {@link android.content.pm.ApplicationInfo#targetSdkVersion targetSdkVersion} + * ≥ {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}:</p> + * + * <ul> + * + * <li>A physical camera status change will trigger {@link #onPhysicalCameraAvailable} + * or {@link #onPhysicalCameraUnavailable} even after the logical camera becomes + * unavailable. A {@link #onCameraUnavailable} call for a logical camera doesn't reset the + * physical cameras' availability status. This makes it possible for an application opening + * the logical camera device to know which physical camera becomes unavailable or available + * to use.</li> + * + * <li>Similar to {@link android.os.Build.VERSION_CODES#TIRAMISU Android 13} and earlier, + * the logical camera's {@link #onCameraAvailable} callback implies all of its physical + * cameras' status become available. {@link #onPhysicalCameraUnavailable} will be called + * for any unavailable physical cameras upon the logical camera becoming available.</li> + * + * </ul> + * + * <p>Given the pipeline nature of the camera capture through {@link + * android.hardware.camera2.CaptureRequest}, there may be frame drops if the application + * requests images from a physical camera of a logical multi-camera and that physical camera + * becomes unavailable. The application should stop requesting directly from an unavailable + * physical camera as soon as {@link #onPhysicalCameraUnavailable} is received, and also be + * ready to robustly handle frame drop errors for requests targeting physical cameras, + * since those errors may arrive before the unavailability callback.</p> + * * <p>The default implementation of this method does nothing.</p> * * @param cameraId The unique identifier of the logical multi-camera. @@ -2283,7 +2364,8 @@ public final class CameraManager { postSingleUpdate(callback, executor, id, null /*physicalId*/, status); // Send the NOT_PRESENT state for unavailable physical cameras - if (isAvailable(status) && mUnavailablePhysicalDevices.containsKey(id)) { + if ((isAvailable(status) || physicalCallbacksAreEnabledForUnavailableCamera()) + && mUnavailablePhysicalDevices.containsKey(id)) { ArrayList<String> unavailableIds = mUnavailablePhysicalDevices.get(id); for (String unavailableId : unavailableIds) { postSingleUpdate(callback, executor, id, unavailableId, @@ -2416,7 +2498,8 @@ public final class CameraManager { return; } - if (!isAvailable(mDeviceStatus.get(id))) { + if (!physicalCallbacksAreEnabledForUnavailableCamera() + && !isAvailable(mDeviceStatus.get(id))) { Log.i(TAG, String.format("Camera %s is not available. Ignore physical camera " + "status change callback(s)", id)); return; diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java index db83e6218daf..2fa8b877a2df 100644 --- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java @@ -101,15 +101,15 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes // Lock to synchronize cross-thread access to device public interface - final Object mInterfaceLock = new Object(); // access from this class and Session only! + final Object mInterfaceLock; /** * @hide */ @RequiresPermission(android.Manifest.permission.CAMERA) public static CameraAdvancedExtensionSessionImpl createCameraAdvancedExtensionSession( - @NonNull CameraDevice cameraDevice, @NonNull Context ctx, - @NonNull ExtensionSessionConfiguration config, int sessionId) + @NonNull android.hardware.camera2.impl.CameraDeviceImpl cameraDevice, + @NonNull Context ctx, @NonNull ExtensionSessionConfiguration config, int sessionId) throws CameraAccessException, RemoteException { long clientId = CameraExtensionCharacteristics.registerClient(ctx); if (clientId < 0) { @@ -209,7 +209,8 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes } private CameraAdvancedExtensionSessionImpl(long extensionClientId, - @NonNull IAdvancedExtenderImpl extender, @NonNull CameraDevice cameraDevice, + @NonNull IAdvancedExtenderImpl extender, + @NonNull android.hardware.camera2.impl.CameraDeviceImpl cameraDevice, @Nullable Surface repeatingRequestSurface, @Nullable Surface burstCaptureSurface, @Nullable Surface postviewSurface, @NonNull CameraExtensionSession.StateCallback callback, @NonNull Executor executor, @@ -228,6 +229,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes mInitialized = false; mInitializeHandler = new InitializeSessionHandler(); mSessionId = sessionId; + mInterfaceLock = cameraDevice.mInterfaceLock; } /** @@ -599,13 +601,14 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes public void onConfigured(@NonNull CameraCaptureSession session) { synchronized (mInterfaceLock) { mCaptureSession = session; - try { - CameraExtensionCharacteristics.initializeSession(mInitializeHandler); - } catch (RemoteException e) { - Log.e(TAG, "Failed to initialize session! Extension service does" - + " not respond!"); - notifyConfigurationFailure(); - } + } + + try { + CameraExtensionCharacteristics.initializeSession(mInitializeHandler); + } catch (RemoteException e) { + Log.e(TAG, "Failed to initialize session! Extension service does" + + " not respond!"); + notifyConfigurationFailure(); } } } @@ -613,46 +616,56 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes private class InitializeSessionHandler extends IInitializeSessionCallback.Stub { @Override public void onSuccess() { - boolean status = true; - synchronized (mInterfaceLock) { - try { - if (mSessionProcessor != null) { - mSessionProcessor.onCaptureSessionStart(mRequestProcessor); - mInitialized = true; - } else { - Log.v(TAG, "Failed to start capture session, session released before " + - "extension start!"); - status = false; - mCaptureSession.close(); + mHandler.post(new Runnable() { + @Override + public void run() { + boolean status = true; + synchronized (mInterfaceLock) { + try { + if (mSessionProcessor != null) { + mSessionProcessor.onCaptureSessionStart(mRequestProcessor); + mInitialized = true; + } else { + Log.v(TAG, "Failed to start capture session, session " + + " released before extension start!"); + status = false; + } + } catch (RemoteException e) { + Log.e(TAG, "Failed to start capture session," + + " extension service does not respond!"); + status = false; + mInitialized = false; + } } - } catch (RemoteException e) { - Log.e(TAG, "Failed to start capture session," - + " extension service does not respond!"); - status = false; - mCaptureSession.close(); - } - } - if (status) { - final long ident = Binder.clearCallingIdentity(); - try { - mExecutor.execute( - () -> mCallbacks.onConfigured(CameraAdvancedExtensionSessionImpl.this)); - } finally { - Binder.restoreCallingIdentity(ident); + if (status) { + final long ident = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallbacks.onConfigured( + CameraAdvancedExtensionSessionImpl.this)); + } finally { + Binder.restoreCallingIdentity(ident); + } + } else { + onFailure(); + } } - } else { - notifyConfigurationFailure(); - } + }); } @Override public void onFailure() { - mCaptureSession.close(); - Log.e(TAG, "Failed to initialize proxy service session!" - + " This can happen when trying to configure multiple " - + "concurrent extension sessions!"); - notifyConfigurationFailure(); + mHandler.post(new Runnable() { + @Override + public void run() { + mCaptureSession.close(); + + Log.e(TAG, "Failed to initialize proxy service session!" + + " This can happen when trying to configure multiple " + + "concurrent extension sessions!"); + notifyConfigurationFailure(); + } + }); } } diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java index c2b36560919b..9c878c78855b 100644 --- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java @@ -117,7 +117,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { private boolean mInternalRepeatingRequestEnabled = true; // Lock to synchronize cross-thread access to device public interface - final Object mInterfaceLock = new Object(); // access from this class and Session only! + final Object mInterfaceLock; private static int nativeGetSurfaceFormat(Surface surface) { return SurfaceUtils.getSurfaceFormat(surface); @@ -128,7 +128,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { */ @RequiresPermission(android.Manifest.permission.CAMERA) public static CameraExtensionSessionImpl createCameraExtensionSession( - @NonNull CameraDevice cameraDevice, + @NonNull android.hardware.camera2.impl.CameraDeviceImpl cameraDevice, @NonNull Context ctx, @NonNull ExtensionSessionConfiguration config, int sessionId) @@ -251,7 +251,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { @NonNull IPreviewExtenderImpl previewExtender, @NonNull List<Size> previewSizes, long extensionClientId, - @NonNull CameraDevice cameraDevice, + @NonNull android.hardware.camera2.impl.CameraDeviceImpl cameraDevice, @Nullable Surface repeatingRequestSurface, @Nullable Surface burstCaptureSurface, @Nullable Surface postviewSurface, @@ -279,6 +279,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { mSupportedRequestKeys = requestKeys; mSupportedResultKeys = resultKeys; mCaptureResultsSupported = !resultKeys.isEmpty(); + mInterfaceLock = cameraDevice.mInterfaceLock; } private void initializeRepeatingRequestPipeline() throws RemoteException { @@ -969,46 +970,56 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { private class InitializeSessionHandler extends IInitializeSessionCallback.Stub { @Override public void onSuccess() { - boolean status = true; - ArrayList<CaptureStageImpl> initialRequestList = - compileInitialRequestList(); - if (!initialRequestList.isEmpty()) { - try { - setInitialCaptureRequest(initialRequestList, - new InitialRequestHandler( - mRepeatingRequestImageCallback)); - } catch (CameraAccessException e) { - Log.e(TAG, - "Failed to initialize the initial capture " - + "request!"); - status = false; - } - } else { - try { - setRepeatingRequest(mPreviewExtender.getCaptureStage(), - new PreviewRequestHandler(null, null, null, - mRepeatingRequestImageCallback)); - } catch (CameraAccessException | RemoteException e) { - Log.e(TAG, - "Failed to initialize internal repeating " - + "request!"); - status = false; - } + mHandler.post(new Runnable() { + @Override + public void run() { + boolean status = true; + ArrayList<CaptureStageImpl> initialRequestList = + compileInitialRequestList(); + if (!initialRequestList.isEmpty()) { + try { + setInitialCaptureRequest(initialRequestList, + new InitialRequestHandler( + mRepeatingRequestImageCallback)); + } catch (CameraAccessException e) { + Log.e(TAG, + "Failed to initialize the initial capture " + + "request!"); + status = false; + } + } else { + try { + setRepeatingRequest(mPreviewExtender.getCaptureStage(), + new PreviewRequestHandler(null, null, null, + mRepeatingRequestImageCallback)); + } catch (CameraAccessException | RemoteException e) { + Log.e(TAG, + "Failed to initialize internal repeating " + + "request!"); + status = false; + } - } + } - if (!status) { - notifyConfigurationFailure(); - } + if (!status) { + notifyConfigurationFailure(); + } + } + }); } @Override public void onFailure() { - mCaptureSession.close(); - Log.e(TAG, "Failed to initialize proxy service session!" - + " This can happen when trying to configure multiple " - + "concurrent extension sessions!"); - notifyConfigurationFailure(); + mHandler.post(new Runnable() { + @Override + public void run() { + mCaptureSession.close(); + Log.e(TAG, "Failed to initialize proxy service session!" + + " This can happen when trying to configure multiple " + + "concurrent extension sessions!"); + notifyConfigurationFailure(); + } + }); } } diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java index a47783cdacfe..6b604422ffba 100644 --- a/core/java/android/view/HandwritingInitiator.java +++ b/core/java/android/view/HandwritingInitiator.java @@ -205,6 +205,16 @@ public class HandwritingInitiator { } /** + * Notify HandwritingInitiator that a delegate view (see {@link View#isHandwritingDelegate}) + * gained focus. + */ + public void onDelegateViewFocused(@NonNull View view) { + if (view == getConnectedView()) { + tryAcceptStylusHandwritingDelegation(view); + } + } + + /** * Notify HandwritingInitiator that a new InputConnection is created. * The caller of this method should guarantee that each onInputConnectionCreated call * is paired with a onInputConnectionClosed call. diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 800fc97d03a6..aec3487910d8 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -8324,6 +8324,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, onFocusLost(); } else if (hasWindowFocus()) { notifyFocusChangeToImeFocusController(true /* hasFocus */); + + if (mIsHandwritingDelegate) { + ViewRootImpl viewRoot = getViewRootImpl(); + if (viewRoot != null) { + viewRoot.getHandwritingInitiator().onDelegateViewFocused(this); + } + } } invalidate(true); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 24dcb69f8342..fb25e7a6bfe1 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -11626,7 +11626,8 @@ public final class ViewRootImpl implements ViewParent, mNumPausedForSync++; mHandler.removeMessages(MSG_PAUSED_FOR_SYNC_TIMEOUT); - mHandler.sendEmptyMessageDelayed(MSG_PAUSED_FOR_SYNC_TIMEOUT, 1000); + mHandler.sendEmptyMessageDelayed(MSG_PAUSED_FOR_SYNC_TIMEOUT, + 1000 * Build.HW_TIMEOUT_MULTIPLIER); return mActiveSurfaceSyncGroup; }; diff --git a/core/java/android/window/SurfaceSyncGroup.java b/core/java/android/window/SurfaceSyncGroup.java index 0672d6318212..7f99fb7e7815 100644 --- a/core/java/android/window/SurfaceSyncGroup.java +++ b/core/java/android/window/SurfaceSyncGroup.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.annotation.UiThread; import android.os.Binder; import android.os.BinderProxy; +import android.os.Build; import android.os.Debug; import android.os.Handler; import android.os.HandlerThread; @@ -62,7 +63,7 @@ public final class SurfaceSyncGroup { private static final int MAX_COUNT = 100; private static final AtomicInteger sCounter = new AtomicInteger(0); - private static final int TRANSACTION_READY_TIMEOUT = 1000; + private static final int TRANSACTION_READY_TIMEOUT = 1000 * Build.HW_TIMEOUT_MULTIPLIER; private static Supplier<Transaction> sTransactionFactory = Transaction::new; diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index a3209f68acf9..fabb08923089 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -131,6 +131,19 @@ public final class WindowContainerTransaction implements Parcelable { } /** + * Sets the densityDpi value in the configuration for the given container. + * @hide + */ + @NonNull + public WindowContainerTransaction setDensityDpi(@NonNull WindowContainerToken container, + int densityDpi) { + Change chg = getOrCreateChange(container.asBinder()); + chg.mConfiguration.densityDpi = densityDpi; + chg.mConfigSetMask |= ActivityInfo.CONFIG_DENSITY; + return this; + } + + /** * Notify {@link com.android.server.wm.PinnedTaskController} that the picture-in-picture task * has finished the enter animation with the given bounds. */ diff --git a/core/java/com/android/internal/app/procstats/DumpUtils.java b/core/java/com/android/internal/app/procstats/DumpUtils.java index bce0d6076d24..f6bcc4661fd6 100644 --- a/core/java/com/android/internal/app/procstats/DumpUtils.java +++ b/core/java/com/android/internal/app/procstats/DumpUtils.java @@ -27,12 +27,12 @@ import static com.android.internal.app.procstats.ProcessStats.ADJ_SCREEN_MOD; import static com.android.internal.app.procstats.ProcessStats.ADJ_SCREEN_OFF; import static com.android.internal.app.procstats.ProcessStats.ADJ_SCREEN_ON; import static com.android.internal.app.procstats.ProcessStats.STATE_BACKUP; -import static com.android.internal.app.procstats.ProcessStats.STATE_BOUND_TOP_OR_FGS; -import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY; -import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY_CLIENT; -import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_EMPTY; +import static com.android.internal.app.procstats.ProcessStats.STATE_BOUND_FGS; +import static com.android.internal.app.procstats.ProcessStats.STATE_BOUND_TOP; +import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED; import static com.android.internal.app.procstats.ProcessStats.STATE_COUNT; import static com.android.internal.app.procstats.ProcessStats.STATE_FGS; +import static com.android.internal.app.procstats.ProcessStats.STATE_FROZEN; import static com.android.internal.app.procstats.ProcessStats.STATE_HEAVY_WEIGHT; import static com.android.internal.app.procstats.ProcessStats.STATE_HOME; import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_BACKGROUND; @@ -72,7 +72,8 @@ public final class DumpUtils { STATE_NAMES = new String[STATE_COUNT]; STATE_NAMES[STATE_PERSISTENT] = "Persist"; STATE_NAMES[STATE_TOP] = "Top"; - STATE_NAMES[STATE_BOUND_TOP_OR_FGS] = "BTopFgs"; + STATE_NAMES[STATE_BOUND_FGS] = "BFgs"; + STATE_NAMES[STATE_BOUND_TOP] = "BTop"; STATE_NAMES[STATE_FGS] = "Fgs"; STATE_NAMES[STATE_IMPORTANT_FOREGROUND] = "ImpFg"; STATE_NAMES[STATE_IMPORTANT_BACKGROUND] = "ImpBg"; @@ -83,14 +84,14 @@ public final class DumpUtils { STATE_NAMES[STATE_HEAVY_WEIGHT] = "HeavyWt"; STATE_NAMES[STATE_HOME] = "Home"; STATE_NAMES[STATE_LAST_ACTIVITY] = "LastAct"; - STATE_NAMES[STATE_CACHED_ACTIVITY] = "CchAct"; - STATE_NAMES[STATE_CACHED_ACTIVITY_CLIENT] = "CchCAct"; - STATE_NAMES[STATE_CACHED_EMPTY] = "CchEmty"; + STATE_NAMES[STATE_CACHED] = "Cached"; + STATE_NAMES[STATE_FROZEN] = "Frozen"; STATE_LABELS = new String[STATE_COUNT]; STATE_LABELS[STATE_PERSISTENT] = "Persistent"; STATE_LABELS[STATE_TOP] = " Top"; - STATE_LABELS[STATE_BOUND_TOP_OR_FGS] = "Bnd TopFgs"; + STATE_LABELS[STATE_BOUND_FGS] = " Bnd Fgs"; + STATE_LABELS[STATE_BOUND_TOP] = " Bnd Top"; STATE_LABELS[STATE_FGS] = " Fgs"; STATE_LABELS[STATE_IMPORTANT_FOREGROUND] = " Imp Fg"; STATE_LABELS[STATE_IMPORTANT_BACKGROUND] = " Imp Bg"; @@ -101,16 +102,16 @@ public final class DumpUtils { STATE_LABELS[STATE_HEAVY_WEIGHT] = " Heavy Wgt"; STATE_LABELS[STATE_HOME] = " (Home)"; STATE_LABELS[STATE_LAST_ACTIVITY] = "(Last Act)"; - STATE_LABELS[STATE_CACHED_ACTIVITY] = " (Cch Act)"; - STATE_LABELS[STATE_CACHED_ACTIVITY_CLIENT] = "(Cch CAct)"; - STATE_LABELS[STATE_CACHED_EMPTY] = "(Cch Emty)"; + STATE_LABELS[STATE_CACHED] = " (Cached)"; + STATE_LABELS[STATE_FROZEN] = " Frozen"; STATE_LABEL_CACHED = " (Cached)"; STATE_LABEL_TOTAL = " TOTAL"; STATE_NAMES_CSV = new String[STATE_COUNT]; STATE_NAMES_CSV[STATE_PERSISTENT] = "pers"; STATE_NAMES_CSV[STATE_TOP] = "top"; - STATE_NAMES_CSV[STATE_BOUND_TOP_OR_FGS] = "btopfgs"; + STATE_NAMES_CSV[STATE_BOUND_FGS] = "bfgs"; + STATE_NAMES_CSV[STATE_BOUND_TOP] = "btop"; STATE_NAMES_CSV[STATE_FGS] = "fgs"; STATE_NAMES_CSV[STATE_IMPORTANT_FOREGROUND] = "impfg"; STATE_NAMES_CSV[STATE_IMPORTANT_BACKGROUND] = "impbg"; @@ -121,14 +122,14 @@ public final class DumpUtils { STATE_NAMES_CSV[STATE_HEAVY_WEIGHT] = "heavy"; STATE_NAMES_CSV[STATE_HOME] = "home"; STATE_NAMES_CSV[STATE_LAST_ACTIVITY] = "lastact"; - STATE_NAMES_CSV[STATE_CACHED_ACTIVITY] = "cch-activity"; - STATE_NAMES_CSV[STATE_CACHED_ACTIVITY_CLIENT] = "cch-aclient"; - STATE_NAMES_CSV[STATE_CACHED_EMPTY] = "cch-empty"; + STATE_NAMES_CSV[STATE_CACHED] = "cached"; + STATE_NAMES_CSV[STATE_FROZEN] = "frzn"; STATE_TAGS = new String[STATE_COUNT]; STATE_TAGS[STATE_PERSISTENT] = "p"; STATE_TAGS[STATE_TOP] = "t"; - STATE_TAGS[STATE_BOUND_TOP_OR_FGS] = "d"; + STATE_TAGS[STATE_BOUND_FGS] = "y"; + STATE_TAGS[STATE_BOUND_TOP] = "z"; STATE_TAGS[STATE_FGS] = "g"; STATE_TAGS[STATE_IMPORTANT_FOREGROUND] = "f"; STATE_TAGS[STATE_IMPORTANT_BACKGROUND] = "b"; @@ -139,15 +140,14 @@ public final class DumpUtils { STATE_TAGS[STATE_HEAVY_WEIGHT] = "w"; STATE_TAGS[STATE_HOME] = "h"; STATE_TAGS[STATE_LAST_ACTIVITY] = "l"; - STATE_TAGS[STATE_CACHED_ACTIVITY] = "a"; - STATE_TAGS[STATE_CACHED_ACTIVITY_CLIENT] = "c"; - STATE_TAGS[STATE_CACHED_EMPTY] = "e"; + STATE_TAGS[STATE_CACHED] = "a"; + STATE_TAGS[STATE_FROZEN] = "e"; STATE_PROTO_ENUMS = new int[STATE_COUNT]; STATE_PROTO_ENUMS[STATE_PERSISTENT] = ProcessStatsEnums.PROCESS_STATE_PERSISTENT; STATE_PROTO_ENUMS[STATE_TOP] = ProcessStatsEnums.PROCESS_STATE_TOP; - STATE_PROTO_ENUMS[STATE_BOUND_TOP_OR_FGS] = - ProcessStatsEnums.PROCESS_STATE_BOUND_TOP_OR_FGS; + STATE_PROTO_ENUMS[STATE_BOUND_FGS] = ProcessStatsEnums.PROCESS_STATE_BOUND_FGS; + STATE_PROTO_ENUMS[STATE_BOUND_TOP] = ProcessStatsEnums.PROCESS_STATE_BOUND_TOP; STATE_PROTO_ENUMS[STATE_FGS] = ProcessStatsEnums.PROCESS_STATE_FGS; STATE_PROTO_ENUMS[STATE_IMPORTANT_FOREGROUND] = ProcessStatsEnums.PROCESS_STATE_IMPORTANT_FOREGROUND; @@ -161,10 +161,8 @@ public final class DumpUtils { STATE_PROTO_ENUMS[STATE_HEAVY_WEIGHT] = ProcessStatsEnums.PROCESS_STATE_HEAVY_WEIGHT; STATE_PROTO_ENUMS[STATE_HOME] = ProcessStatsEnums.PROCESS_STATE_HOME; STATE_PROTO_ENUMS[STATE_LAST_ACTIVITY] = ProcessStatsEnums.PROCESS_STATE_LAST_ACTIVITY; - STATE_PROTO_ENUMS[STATE_CACHED_ACTIVITY] = ProcessStatsEnums.PROCESS_STATE_CACHED_ACTIVITY; - STATE_PROTO_ENUMS[STATE_CACHED_ACTIVITY_CLIENT] = - ProcessStatsEnums.PROCESS_STATE_CACHED_ACTIVITY_CLIENT; - STATE_PROTO_ENUMS[STATE_CACHED_EMPTY] = ProcessStatsEnums.PROCESS_STATE_CACHED_EMPTY; + STATE_PROTO_ENUMS[STATE_CACHED] = ProcessStatsEnums.PROCESS_STATE_CACHED_ACTIVITY; + STATE_PROTO_ENUMS[STATE_FROZEN] = ProcessStatsEnums.PROCESS_STATE_FROZEN; // Remap states, as defined by ProcessStats.java, to a reduced subset of states for data // aggregation / size reduction purposes. @@ -173,7 +171,9 @@ public final class DumpUtils { ProcessStatsEnums.AGGREGATED_PROCESS_STATE_PERSISTENT; PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_TOP] = ProcessStatsEnums.AGGREGATED_PROCESS_STATE_TOP; - PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_BOUND_TOP_OR_FGS] = + PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_BOUND_FGS] = + ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BOUND_TOP_OR_FGS; + PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_BOUND_TOP] = ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BOUND_TOP_OR_FGS; PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_FGS] = ProcessStatsEnums.AGGREGATED_PROCESS_STATE_FGS; @@ -196,11 +196,9 @@ public final class DumpUtils { ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED; PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_LAST_ACTIVITY] = ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED; - PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_ACTIVITY] = - ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED; - PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_ACTIVITY_CLIENT] = + PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED] = ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED; - PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_EMPTY] = + PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_FROZEN] = ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED; } diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java index 818a50366115..fff778c616ee 100644 --- a/core/java/com/android/internal/app/procstats/ProcessState.java +++ b/core/java/com/android/internal/app/procstats/ProcessState.java @@ -28,10 +28,9 @@ import static com.android.internal.app.procstats.ProcessStats.PSS_USS_AVERAGE; import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MAXIMUM; import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MINIMUM; import static com.android.internal.app.procstats.ProcessStats.STATE_BACKUP; -import static com.android.internal.app.procstats.ProcessStats.STATE_BOUND_TOP_OR_FGS; -import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY; -import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY_CLIENT; -import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_EMPTY; +import static com.android.internal.app.procstats.ProcessStats.STATE_BOUND_FGS; +import static com.android.internal.app.procstats.ProcessStats.STATE_BOUND_TOP; +import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED; import static com.android.internal.app.procstats.ProcessStats.STATE_COUNT; import static com.android.internal.app.procstats.ProcessStats.STATE_FGS; import static com.android.internal.app.procstats.ProcessStats.STATE_HEAVY_WEIGHT; @@ -85,9 +84,9 @@ public final class ProcessState { STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT_UI STATE_TOP, // ActivityManager.PROCESS_STATE_TOP - STATE_BOUND_TOP_OR_FGS, // ActivityManager.PROCESS_STATE_BOUND_TOP + STATE_BOUND_TOP, // ActivityManager.PROCESS_STATE_BOUND_TOP STATE_FGS, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE - STATE_BOUND_TOP_OR_FGS, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE + STATE_BOUND_FGS, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND STATE_IMPORTANT_BACKGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND STATE_IMPORTANT_BACKGROUND, // ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND @@ -98,10 +97,10 @@ public final class ProcessState { STATE_HEAVY_WEIGHT, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT STATE_HOME, // ActivityManager.PROCESS_STATE_HOME STATE_LAST_ACTIVITY, // ActivityManager.PROCESS_STATE_LAST_ACTIVITY - STATE_CACHED_ACTIVITY, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY - STATE_CACHED_ACTIVITY_CLIENT, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT - STATE_CACHED_ACTIVITY, // ActivityManager.PROCESS_STATE_CACHED_RECENT - STATE_CACHED_EMPTY, // ActivityManager.PROCESS_STATE_CACHED_EMPTY + STATE_CACHED, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY + STATE_CACHED, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT + STATE_CACHED, // ActivityManager.PROCESS_STATE_CACHED_RECENT + STATE_CACHED, // ActivityManager.PROCESS_STATE_CACHED_EMPTY }; public static final Comparator<ProcessState> COMPARATOR = new Comparator<ProcessState>() { @@ -926,8 +925,11 @@ public final class ProcessState { screenStates, memStates, new int[] { STATE_PERSISTENT }, now, totalTime, true); dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_TOP], screenStates, memStates, new int[] {STATE_TOP}, now, totalTime, true); - dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_BOUND_TOP_OR_FGS], - screenStates, memStates, new int[] { STATE_BOUND_TOP_OR_FGS}, now, totalTime, + dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_BOUND_TOP], + screenStates, memStates, new int[] { STATE_BOUND_TOP }, now, totalTime, + true); + dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_BOUND_FGS], + screenStates, memStates, new int[] { STATE_BOUND_FGS }, now, totalTime, true); dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_FGS], screenStates, memStates, new int[] { STATE_FGS}, now, totalTime, @@ -953,9 +955,6 @@ public final class ProcessState { screenStates, memStates, new int[] {STATE_HOME}, now, totalTime, true); dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_LAST_ACTIVITY], screenStates, memStates, new int[] {STATE_LAST_ACTIVITY}, now, totalTime, true); - dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABEL_CACHED, - screenStates, memStates, new int[] {STATE_CACHED_ACTIVITY, - STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY}, now, totalTime, true); } public void dumpProcessState(PrintWriter pw, String prefix, @@ -1563,7 +1562,10 @@ public final class ProcessState { case STATE_TOP: topMs += duration; break; - case STATE_BOUND_TOP_OR_FGS: + case STATE_BOUND_FGS: + boundFgsMs += duration; + break; + case STATE_BOUND_TOP: boundTopMs += duration; break; case STATE_FGS: @@ -1583,13 +1585,10 @@ public final class ProcessState { case STATE_PERSISTENT: otherMs += duration; break; - case STATE_CACHED_ACTIVITY: - case STATE_CACHED_ACTIVITY_CLIENT: - case STATE_CACHED_EMPTY: + case STATE_CACHED: cachedMs += duration; break; - // TODO (b/261910877) Add support for tracking boundFgsMs and - // frozenMs. + // TODO (b/261910877) Add support for tracking frozenMs. } } statsEventOutput.write( diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java index f3ed09a861e3..3ce234b4167b 100644 --- a/core/java/com/android/internal/app/procstats/ProcessStats.java +++ b/core/java/com/android/internal/app/procstats/ProcessStats.java @@ -81,21 +81,21 @@ public final class ProcessStats implements Parcelable { public static final int STATE_NOTHING = -1; public static final int STATE_PERSISTENT = 0; public static final int STATE_TOP = 1; - public static final int STATE_BOUND_TOP_OR_FGS = 2; + public static final int STATE_BOUND_TOP = 2; public static final int STATE_FGS = 3; - public static final int STATE_IMPORTANT_FOREGROUND = 4; - public static final int STATE_IMPORTANT_BACKGROUND = 5; - public static final int STATE_BACKUP = 6; - public static final int STATE_SERVICE = 7; - public static final int STATE_SERVICE_RESTARTING = 8; - public static final int STATE_RECEIVER = 9; - public static final int STATE_HEAVY_WEIGHT = 10; - public static final int STATE_HOME = 11; - public static final int STATE_LAST_ACTIVITY = 12; - public static final int STATE_CACHED_ACTIVITY = 13; - public static final int STATE_CACHED_ACTIVITY_CLIENT = 14; - public static final int STATE_CACHED_EMPTY = 15; - public static final int STATE_COUNT = STATE_CACHED_EMPTY+1; + public static final int STATE_BOUND_FGS = 4; + public static final int STATE_IMPORTANT_FOREGROUND = 5; + public static final int STATE_IMPORTANT_BACKGROUND = 6; + public static final int STATE_BACKUP = 7; + public static final int STATE_SERVICE = 8; + public static final int STATE_SERVICE_RESTARTING = 9; + public static final int STATE_RECEIVER = 10; + public static final int STATE_HEAVY_WEIGHT = 11; + public static final int STATE_HOME = 12; + public static final int STATE_LAST_ACTIVITY = 13; + public static final int STATE_CACHED = 14; + public static final int STATE_FROZEN = 15; + public static final int STATE_COUNT = STATE_FROZEN + 1; public static final int PSS_SAMPLE_COUNT = 0; public static final int PSS_MINIMUM = 1; @@ -154,9 +154,10 @@ public final class ProcessStats implements Parcelable { public static final int[] ALL_SCREEN_ADJ = new int[] { ADJ_SCREEN_OFF, ADJ_SCREEN_ON }; public static final int[] NON_CACHED_PROC_STATES = new int[] { - STATE_PERSISTENT, STATE_TOP, STATE_BOUND_TOP_OR_FGS, STATE_FGS, + STATE_PERSISTENT, STATE_TOP, STATE_FGS, STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, - STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER, STATE_HEAVY_WEIGHT + STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER, STATE_HEAVY_WEIGHT, + STATE_BOUND_TOP, STATE_BOUND_FGS }; public static final int[] BACKGROUND_PROC_STATES = new int[] { @@ -165,11 +166,11 @@ public final class ProcessStats implements Parcelable { }; public static final int[] ALL_PROC_STATES = new int[] { STATE_PERSISTENT, - STATE_TOP, STATE_BOUND_TOP_OR_FGS, STATE_FGS, STATE_IMPORTANT_FOREGROUND, + STATE_TOP, STATE_FGS, STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER, - STATE_HEAVY_WEIGHT, STATE_HOME, STATE_LAST_ACTIVITY, STATE_CACHED_ACTIVITY, - STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY + STATE_HEAVY_WEIGHT, STATE_HOME, STATE_LAST_ACTIVITY, STATE_CACHED, + STATE_BOUND_TOP, STATE_BOUND_FGS, STATE_FROZEN }; // Should report process stats. diff --git a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java index 1ec2613dd101..fccb177dad4f 100644 --- a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java +++ b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java @@ -24,6 +24,7 @@ import static android.view.stylus.HandwritingTestUtil.createView; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -224,7 +225,7 @@ public class HandwritingInitiatorTest { } @Test - public void onTouchEvent_startHandwriting_delegate() { + public void onTouchEvent_tryAcceptDelegation_delegatorCallbackCreatesInputConnection() { View delegateView = new View(mContext); delegateView.setIsHandwritingDelegate(true); @@ -245,6 +246,29 @@ public class HandwritingInitiatorTest { } @Test + public void onTouchEvent_tryAcceptDelegation_delegatorCallbackFocusesDelegate() { + View delegateView = new View(mContext); + delegateView.setIsHandwritingDelegate(true); + mHandwritingInitiator.onInputConnectionCreated(delegateView); + reset(mHandwritingInitiator); + + mTestView1.setHandwritingDelegatorCallback( + () -> mHandwritingInitiator.onDelegateViewFocused(delegateView)); + + final int x1 = (sHwArea1.left + sHwArea1.right) / 2; + final int y1 = (sHwArea1.top + sHwArea1.bottom) / 2; + MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0); + mHandwritingInitiator.onTouchEvent(stylusEvent1); + + final int x2 = x1 + mHandwritingSlop * 2; + final int y2 = y1; + MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0); + mHandwritingInitiator.onTouchEvent(stylusEvent2); + + verify(mHandwritingInitiator, times(1)).tryAcceptStylusHandwritingDelegation(delegateView); + } + + @Test public void onTouchEvent_notStartHandwriting_whenHandwritingNotAvailable() { final Rect rect = new Rect(600, 600, 900, 900); final View testView = createView(rect, true /* autoHandwritingEnabled */, diff --git a/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java b/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java index 35b3267ea301..61899143b9c5 100644 --- a/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java @@ -23,6 +23,8 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.MockitoAnnotations.initMocks; +import android.app.ActivityManager; + import androidx.test.filters.SmallTest; import com.android.internal.util.FrameworkStatsLog; @@ -128,6 +130,34 @@ public class ProcessStatsTest extends TestCase { } @SmallTest + public void testDumpBoundFgsDuration() throws Exception { + ProcessStats processStats = new ProcessStats(); + ProcessState processState = + processStats.getProcessStateLocked( + APP_1_PACKAGE_NAME, APP_1_UID, APP_1_VERSION, APP_1_PROCESS_NAME); + processState.setState(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE, + ProcessStats.ADJ_MEM_FACTOR_NORMAL, NOW_MS, /* pkgList */ null); + processState.commitStateTime(NOW_MS + TimeUnit.SECONDS.toMillis(DURATION_SECS)); + processStats.dumpProcessState(FrameworkStatsLog.PROCESS_STATE, mStatsEventOutput); + verify(mStatsEventOutput) + .write( + eq(FrameworkStatsLog.PROCESS_STATE), + eq(APP_1_UID), + eq(APP_1_PROCESS_NAME), + anyInt(), + anyInt(), + eq(0), + eq(0), + eq(0), + eq(0), + eq(DURATION_SECS), + eq(0), + eq(0), + eq(0), + eq(0)); + } + + @SmallTest public void testDumpProcessAssociation() throws Exception { ProcessStats processStats = new ProcessStats(); AssociationState associationState = diff --git a/data/keyboards/Vendor_004c_Product_0265.idc b/data/keyboards/Vendor_004c_Product_0265.idc index 707dfcfe3a10..bfea4db747c2 120000..100644 --- a/data/keyboards/Vendor_004c_Product_0265.idc +++ b/data/keyboards/Vendor_004c_Product_0265.idc @@ -1 +1,34 @@ -Vendor_05ac_Product_0265.idc
\ No newline at end of file +# Copyright 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Apple Magic Trackpad 2 (Bluetooth) configuration file +# +# WHEN MODIFYING, also change the USB file (Vendor_05ac_Product_0265.idc) +# + +gestureProp.Pressure_Calibration_Offset = 30 +gestureProp.Palm_Pressure = 250.0 +gestureProp.Palm_Width = 20.0 +gestureProp.Multiple_Palm_Width = 20.0 + +# Enable Stationary Wiggle Filter +gestureProp.Stationary_Wiggle_Filter_Enabled = 1 +gestureProp.Finger_Moving_Energy = 0.0008 +gestureProp.Finger_Moving_Hysteresis = 0.0004 + +# Avoid accidental scroll/move on finger lift +gestureProp.Max_Stationary_Move_Speed = 47 +gestureProp.Max_Stationary_Move_Speed_Hysteresis = 1 +gestureProp.Max_Stationary_Move_Suppress_Distance = 0.2 diff --git a/data/keyboards/Vendor_05ac_Product_0265.idc b/data/keyboards/Vendor_05ac_Product_0265.idc index d72de649c0ed..520d188c71fb 100644 --- a/data/keyboards/Vendor_05ac_Product_0265.idc +++ b/data/keyboards/Vendor_05ac_Product_0265.idc @@ -13,9 +13,9 @@ # limitations under the License. # -# Apple Magic Trackpad 2 configuration file -# Bluetooth vendor ID 004c -# USB vendor ID 05ac +# Apple Magic Trackpad 2 (USB) configuration file +# +# WHEN MODIFYING, also change the Bluetooth file (Vendor_004c_Product_0265.idc) # gestureProp.Pressure_Calibration_Offset = 30 diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 73a740381090..31c5e33f21e3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -25,6 +25,7 @@ import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED import android.app.WindowConfiguration.WindowingMode import android.content.Context import android.os.IBinder +import android.os.SystemProperties import android.view.SurfaceControl import android.view.WindowManager.TRANSIT_CHANGE import android.view.WindowManager.TRANSIT_NONE @@ -32,6 +33,7 @@ import android.view.WindowManager.TRANSIT_OPEN import android.view.WindowManager.TRANSIT_TO_FRONT import android.window.TransitionInfo import android.window.TransitionRequestInfo +import android.window.WindowContainerToken import android.window.WindowContainerTransaction import androidx.annotation.BinderThread import com.android.internal.protolog.common.ProtoLog @@ -115,10 +117,7 @@ class DesktopTasksController( val wct = WindowContainerTransaction() // Bring other apps to front first bringDesktopAppsToFront(wct) - - wct.setWindowingMode(task.getToken(), WINDOWING_MODE_FREEFORM) - wct.reorder(task.getToken(), true /* onTop */) - + addMoveToDesktopChanges(wct, task.token) if (Transitions.ENABLE_SHELL_TRANSITIONS) { transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */) } else { @@ -136,8 +135,7 @@ class DesktopTasksController( ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveToFullscreen: %d", task.taskId) val wct = WindowContainerTransaction() - wct.setWindowingMode(task.getToken(), WINDOWING_MODE_FULLSCREEN) - wct.setBounds(task.getToken(), null) + addMoveToFullscreenChanges(wct, task.token) if (Transitions.ENABLE_SHELL_TRANSITIONS) { transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */) } else { @@ -234,8 +232,8 @@ class DesktopTasksController( " taskId=%d", task.taskId ) - return WindowContainerTransaction().apply { - setWindowingMode(task.token, WINDOWING_MODE_FREEFORM) + return WindowContainerTransaction().also { wct -> + addMoveToDesktopChanges(wct, task.token) } } } @@ -251,15 +249,44 @@ class DesktopTasksController( " taskId=%d", task.taskId ) - return WindowContainerTransaction().apply { - setWindowingMode(task.token, WINDOWING_MODE_FULLSCREEN) - setBounds(task.token, null) + return WindowContainerTransaction().also { wct -> + addMoveToFullscreenChanges(wct, task.token) } } } return null } + private fun addMoveToDesktopChanges( + wct: WindowContainerTransaction, + token: WindowContainerToken + ) { + wct.setWindowingMode(token, WINDOWING_MODE_FREEFORM) + wct.reorder(token, true /* onTop */) + if (isDesktopDensityOverrideSet()) { + wct.setDensityDpi(token, getDesktopDensityDpi()) + } + } + + private fun addMoveToFullscreenChanges( + wct: WindowContainerTransaction, + token: WindowContainerToken + ) { + wct.setWindowingMode(token, WINDOWING_MODE_FULLSCREEN) + wct.setBounds(token, null) + if (isDesktopDensityOverrideSet()) { + wct.setDensityDpi(token, getFullscreenDensityDpi()) + } + } + + private fun getFullscreenDensityDpi(): Int { + return context.resources.displayMetrics.densityDpi + } + + private fun getDesktopDensityDpi(): Int { + return DESKTOP_DENSITY_OVERRIDE + } + /** Creates a new instance of the external interface to pass to another process. */ private fun createExternalInterface(): ExternalInterfaceBinder { return IDesktopModeImpl(this) @@ -318,4 +345,18 @@ class DesktopTasksController( return result[0] } } + + companion object { + private val DESKTOP_DENSITY_OVERRIDE = + SystemProperties.getInt("persist.wm.debug.desktop_mode_density", 0) + private val DESKTOP_DENSITY_ALLOWED_RANGE = (100..1000) + + /** + * Check if desktop density override is enabled + */ + @JvmStatic + fun isDesktopDensityOverrideSet(): Boolean { + return DESKTOP_DENSITY_OVERRIDE in DESKTOP_DENSITY_ALLOWED_RANGE + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java index 4b5ad1d05c4a..e09c3c9e4d3f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java @@ -68,6 +68,7 @@ class SplitScreenTransitions { DismissTransition mPendingDismiss = null; TransitSession mPendingEnter = null; + TransitSession mPendingRecent = null; TransitSession mPendingResize = null; private IBinder mAnimatingTransition = null; @@ -259,6 +260,10 @@ class SplitScreenTransitions { return mPendingEnter != null && mPendingEnter.mTransition == transition; } + boolean isPendingRecent(IBinder transition) { + return mPendingRecent != null && mPendingRecent.mTransition == transition; + } + boolean isPendingDismiss(IBinder transition) { return mPendingDismiss != null && mPendingDismiss.mTransition == transition; } @@ -271,6 +276,8 @@ class SplitScreenTransitions { private TransitSession getPendingTransition(IBinder transition) { if (isPendingEnter(transition)) { return mPendingEnter; + } else if (isPendingRecent(transition)) { + return mPendingRecent; } else if (isPendingDismiss(transition)) { return mPendingDismiss; } else if (isPendingResize(transition)) { @@ -354,10 +361,32 @@ class SplitScreenTransitions { + " deduced Resize split screen"); } + void setRecentTransition(@NonNull IBinder transition, + @Nullable RemoteTransition remoteTransition, + @Nullable TransitionFinishedCallback finishCallback) { + mPendingRecent = new TransitSession(transition, null /* consumedCb */, finishCallback); + + if (remoteTransition != null) { + // Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff) + mPendingRemoteHandler = new OneShotRemoteHandler( + mTransitions.getMainExecutor(), remoteTransition); + mPendingRemoteHandler.setTransition(transition); + } + + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition " + + " deduced Enter recent panel"); + } + void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t, IBinder mergeTarget, Transitions.TransitionFinishCallback finishCallback) { if (mergeTarget != mAnimatingTransition) return; + if (isPendingEnter(transition) && isPendingRecent(mergeTarget)) { + // Since there's an entering transition merged, recent transition no longer + // need to handle entering split screen after the transition finished. + mPendingRecent.setFinishedCallback(null); + } + if (mActiveRemoteHandler != null) { mActiveRemoteHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); } else { @@ -394,6 +423,10 @@ class SplitScreenTransitions { } else if (isPendingDismiss(transition)) { mPendingDismiss.onConsumed(aborted); mPendingDismiss = null; + } else if (isPendingRecent(transition)) { + mPendingRecent.onConsumed(aborted); + mPendingRecent = null; + mPendingRemoteHandler = null; } else if (isPendingResize(transition)) { mPendingResize.onConsumed(aborted); mPendingResize = null; @@ -407,6 +440,9 @@ class SplitScreenTransitions { if (isPendingEnter(mAnimatingTransition)) { mPendingEnter.onFinished(wct, mFinishTransaction); mPendingEnter = null; + } else if (isPendingRecent(mAnimatingTransition)) { + mPendingRecent.onFinished(wct, mFinishTransaction); + mPendingRecent = null; } else if (isPendingDismiss(mAnimatingTransition)) { mPendingDismiss.onFinished(wct, mFinishTransaction); mPendingDismiss = null; @@ -516,7 +552,7 @@ class SplitScreenTransitions { } /** Calls when the transition finished. */ - public interface TransitionFinishedCallback { + interface TransitionFinishedCallback { void onFinished(WindowContainerTransaction wct, SurfaceControl.Transaction t); } 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 9d1a7ea14900..a5546e554422 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 @@ -259,6 +259,37 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } }; + private final SplitScreenTransitions.TransitionFinishedCallback + mRecentTransitionFinishedCallback = + new SplitScreenTransitions.TransitionFinishedCallback() { + @Override + public void onFinished(WindowContainerTransaction finishWct, + SurfaceControl.Transaction finishT) { + // Check if the recent transition is finished by returning to the current + // split, so we + // can restore the divider bar. + for (int i = 0; i < finishWct.getHierarchyOps().size(); ++i) { + final WindowContainerTransaction.HierarchyOp op = + finishWct.getHierarchyOps().get(i); + final IBinder container = op.getContainer(); + if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop() + && (mMainStage.containsContainer(container) + || mSideStage.containsContainer(container))) { + updateSurfaceBounds(mSplitLayout, finishT, + false /* applyResizingOffset */); + setDividerVisibility(true, finishT); + return; + } + } + + // Dismiss the split screen if it's not returning to split. + prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, finishWct); + setSplitsVisible(false); + setDividerVisibility(false, finishT); + logExit(EXIT_REASON_UNKNOWN); + } + }; + protected StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, ShellTaskOrganizer taskOrganizer, DisplayController displayController, DisplayImeController displayImeController, @@ -357,11 +388,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return mMainStage.isActive(); } - /** Checks if `transition` is a pending enter-split transition. */ - public boolean isPendingEnter(IBinder transition) { - return mSplitTransitions.isPendingEnter(transition); - } - @StageType int getStageOfTask(int taskId) { if (mMainStage.containsTask(taskId)) { @@ -2240,8 +2266,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, final int activityType = triggerTask.getActivityType(); if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) { - // starting recents, so don't handle this. - return null; + // Enter overview panel, so start recent transition. + mSplitTransitions.setRecentTransition(transition, request.getRemoteTransition(), + mRecentTransitionFinishedCallback); } } } else { @@ -2370,6 +2397,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mSplitTransitions.isPendingEnter(transition)) { shouldAnimate = startPendingEnterAnimation( transition, info, startTransaction, finishTransaction); + } else if (mSplitTransitions.isPendingRecent(transition)) { + shouldAnimate = startPendingRecentAnimation(transition, info, startTransaction); } else if (mSplitTransitions.isPendingDismiss(transition)) { shouldAnimate = startPendingDismissAnimation( mSplitTransitions.mPendingDismiss, info, startTransaction, finishTransaction); @@ -2624,35 +2653,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return true; } - /** Call this when starting the open-recents animation while split-screen is active. */ - public void onRecentsInSplitAnimationStart(@NonNull SurfaceControl.Transaction t) { + private boolean startPendingRecentAnimation(@NonNull IBinder transition, + @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) { setDividerVisibility(false, t); - } - - /** Call this when the recents animation during split-screen finishes. */ - public void onRecentsInSplitAnimationFinish(WindowContainerTransaction finishWct, - SurfaceControl.Transaction finishT) { - // Check if the recent transition is finished by returning to the current - // split, so we can restore the divider bar. - for (int i = 0; i < finishWct.getHierarchyOps().size(); ++i) { - final WindowContainerTransaction.HierarchyOp op = - finishWct.getHierarchyOps().get(i); - final IBinder container = op.getContainer(); - if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop() - && (mMainStage.containsContainer(container) - || mSideStage.containsContainer(container))) { - updateSurfaceBounds(mSplitLayout, finishT, - false /* applyResizingOffset */); - setDividerVisibility(true, finishT); - return; - } - } - - // Dismiss the split screen if it's not returning to split. - prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, finishWct); - setSplitsVisible(false); - setDividerVisibility(false, finishT); - logExit(EXIT_REASON_UNKNOWN); + return true; } private void addDividerBarToTransition(@NonNull TransitionInfo info, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java index b9f6a012efaf..2e864483bf1d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -18,7 +18,6 @@ package com.android.wm.shell.transition; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; @@ -26,7 +25,6 @@ import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP; -import static com.android.wm.shell.util.TransitionUtil.isOpeningType; import android.annotation.NonNull; import android.annotation.Nullable; @@ -70,20 +68,14 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler { /** Pip was entered while handling an intent with its own remoteTransition. */ static final int TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE = 3; - /** Recents transition while split-screen active. */ - static final int TYPE_RECENTS_DURING_SPLIT = 4; - /** The default animation for this mixed transition. */ static final int ANIM_TYPE_DEFAULT = 0; /** For ENTER_PIP_FROM_SPLIT, indicates that this is a to-home animation. */ static final int ANIM_TYPE_GOING_HOME = 1; - /** For RECENTS_DURING_SPLIT, is set when this turns into a pair->pair task switch. */ - static final int ANIM_TYPE_PAIR_TO_PAIR = 1; - final int mType; - int mAnimType = ANIM_TYPE_DEFAULT; + int mAnimType = 0; final IBinder mTransition; Transitions.TransitionHandler mLeftoversHandler = null; @@ -175,25 +167,6 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler { mixed.mLeftoversHandler = handler.first; mActiveTransitions.add(mixed); return handler.second; - } else if (mSplitHandler.isSplitActive() - && isOpeningType(request.getType()) - && request.getTriggerTask() != null - && request.getTriggerTask().getWindowingMode() == WINDOWING_MODE_FULLSCREEN - && (request.getTriggerTask().getActivityType() == ACTIVITY_TYPE_HOME - || request.getTriggerTask().getActivityType() == ACTIVITY_TYPE_RECENTS)) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while " - + "Split-Screen is active, so treat it as Mixed."); - Pair<Transitions.TransitionHandler, WindowContainerTransaction> handler = - mPlayer.dispatchRequest(transition, request, this); - if (handler == null) { - // fall through -- it will probably be picked-up by normal split handler. - return null; - } - final MixedTransition mixed = new MixedTransition( - MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition); - mixed.mLeftoversHandler = handler.first; - mActiveTransitions.add(mixed); - return handler.second; } return null; } @@ -243,9 +216,6 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler { } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { return animateOpenIntentWithRemoteAndPip(mixed, info, startTransaction, finishTransaction, finishCallback); - } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) { - return animateRecentsDuringSplit(mixed, info, startTransaction, finishTransaction, - finishCallback); } else { mActiveTransitions.remove(mixed); throw new IllegalStateException("Starting mixed animation without a known mixed type? " @@ -471,40 +441,12 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler { return true; } - private boolean animateRecentsDuringSplit(@NonNull final MixedTransition mixed, - @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback) { - // Split-screen is only interested in the recents transition finishing (and merging), so - // just wrap finish and start recents animation directly. - Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> { - mixed.mInFlightSubAnimations = 0; - mActiveTransitions.remove(mixed); - // If pair-to-pair switching, the post-recents clean-up isn't needed. - if (mixed.mAnimType != MixedTransition.ANIM_TYPE_PAIR_TO_PAIR) { - wct = wct != null ? wct : new WindowContainerTransaction(); - mSplitHandler.onRecentsInSplitAnimationFinish(wct, finishTransaction); - } - mSplitHandler.onTransitionAnimationComplete(); - finishCallback.onTransitionFinished(wct, wctCB); - }; - mixed.mInFlightSubAnimations = 1; - mSplitHandler.onRecentsInSplitAnimationStart(startTransaction); - final boolean handled = mixed.mLeftoversHandler.startAnimation(mixed.mTransition, info, - startTransaction, finishTransaction, finishCB); - if (!handled) { - mActiveTransitions.remove(mixed); - } - return handled; - } - @Override public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, @NonNull Transitions.TransitionFinishCallback finishCallback) { for (int i = 0; i < mActiveTransitions.size(); ++i) { - if (mActiveTransitions.get(i).mTransition != mergeTarget) continue; + if (mActiveTransitions.get(i) != mergeTarget) continue; MixedTransition mixed = mActiveTransitions.get(i); if (mixed.mInFlightSubAnimations <= 0) { // Already done, so no need to end it. @@ -532,14 +474,6 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler { mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); } - } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) { - if (mSplitHandler.isPendingEnter(transition)) { - // Recents -> enter-split means that we are switching from one pair to - // another pair. - mixed.mAnimType = MixedTransition.ANIM_TYPE_PAIR_TO_PAIR; - } - mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, - finishCallback); } else { throw new IllegalStateException("Playing a mixed transition with unknown type? " + mixed.mType); @@ -559,8 +493,6 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler { if (mixed == null) return; if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) { mPipHandler.onTransitionConsumed(transition, aborted, finishT); - } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) { - mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index 72da1089c91c..3c0ef965f4f5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -23,6 +23,7 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.ColorStateList; +import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Color; import android.graphics.Point; @@ -46,6 +47,7 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.desktopmode.DesktopModeStatus; +import com.android.wm.shell.desktopmode.DesktopTasksController; /** * Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with @@ -95,6 +97,17 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mDesktopActive = DesktopModeStatus.isActive(mContext); } + @Override + protected Configuration getConfigurationWithOverrides( + ActivityManager.RunningTaskInfo taskInfo) { + Configuration configuration = taskInfo.getConfiguration(); + if (DesktopTasksController.isDesktopDensityOverrideSet()) { + // Density is overridden for desktop tasks. Keep system density for window decoration. + configuration.densityDpi = mContext.getResources().getConfiguration().densityDpi; + } + return configuration; + } + void setCaptionListeners( View.OnClickListener onCaptionButtonClickListener, View.OnTouchListener onCaptionTouchListener) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java index 7a7ac476879e..ddd3b440e1a6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java @@ -131,7 +131,17 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mSurfaceControlViewHostFactory = surfaceControlViewHostFactory; mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId); - mDecorWindowContext = mContext.createConfigurationContext(mTaskInfo.getConfiguration()); + mDecorWindowContext = mContext.createConfigurationContext( + getConfigurationWithOverrides(mTaskInfo)); + } + + /** + * Get {@link Configuration} from supplied {@link RunningTaskInfo}. + * + * Allows values to be overridden before returning the configuration. + */ + protected Configuration getConfigurationWithOverrides(RunningTaskInfo taskInfo) { + return taskInfo.getConfiguration(); } /** @@ -165,7 +175,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> outResult.mRootView = rootView; rootView = null; // Clear it just in case we use it accidentally - final Configuration taskConfig = mTaskInfo.getConfiguration(); + final Configuration taskConfig = getConfigurationWithOverrides(mTaskInfo); if (oldTaskConfig.densityDpi != taskConfig.densityDpi || mDisplay == null || mDisplay.getDisplayId() != mTaskInfo.displayId) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java index 2edec7db828f..3901dabcaec8 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java @@ -35,7 +35,6 @@ import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_P import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -250,7 +249,7 @@ public class SplitTransitionTests extends ShellTestCase { @Test @UiThreadTest - public void testEnterRecentsAndCommit() { + public void testEnterRecents() { enterSplit(); ActivityManager.RunningTaskInfo homeTask = new TestRunningTaskInfoBuilder() @@ -262,60 +261,24 @@ public class SplitTransitionTests extends ShellTestCase { TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_TO_FRONT, homeTask, null); IBinder transition = mock(IBinder.class); WindowContainerTransaction result = mStageCoordinator.handleRequest(transition, request); - // Don't handle recents opening - assertNull(result); - // make sure we haven't made any local changes yet (need to wait until transition is ready) - assertTrue(mStageCoordinator.isSplitScreenVisible()); - - // simulate the start of recents transition - mMainStage.onTaskVanished(mMainChild); - mSideStage.onTaskVanished(mSideChild); - mStageCoordinator.onRecentsInSplitAnimationStart(mock(SurfaceControl.Transaction.class)); - assertTrue(mStageCoordinator.isSplitScreenVisible()); - - // Make sure it cleans-up if recents doesn't restore - WindowContainerTransaction commitWCT = new WindowContainerTransaction(); - mStageCoordinator.onRecentsInSplitAnimationFinish(commitWCT, - mock(SurfaceControl.Transaction.class)); - assertFalse(mStageCoordinator.isSplitScreenVisible()); - } - - @Test - @UiThreadTest - public void testEnterRecentsAndRestore() { - enterSplit(); - - ActivityManager.RunningTaskInfo homeTask = new TestRunningTaskInfoBuilder() - .setWindowingMode(WINDOWING_MODE_FULLSCREEN) - .setActivityType(ACTIVITY_TYPE_HOME) - .build(); - - // Create a request to bring home forward - TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_TO_FRONT, homeTask, null); - IBinder transition = mock(IBinder.class); - WindowContainerTransaction result = mStageCoordinator.handleRequest(transition, request); - // Don't handle recents opening - assertNull(result); + assertTrue(result.isEmpty()); // make sure we haven't made any local changes yet (need to wait until transition is ready) assertTrue(mStageCoordinator.isSplitScreenVisible()); - // simulate the start of recents transition + // simulate the transition + TransitionInfo info = new TransitionInfoBuilder(TRANSIT_TO_FRONT, 0) + .addChange(TRANSIT_TO_FRONT, homeTask) + .addChange(TRANSIT_TO_BACK, mMainChild) + .addChange(TRANSIT_TO_BACK, mSideChild) + .build(); mMainStage.onTaskVanished(mMainChild); mSideStage.onTaskVanished(mSideChild); - mStageCoordinator.onRecentsInSplitAnimationStart(mock(SurfaceControl.Transaction.class)); - assertTrue(mStageCoordinator.isSplitScreenVisible()); - - // Make sure we remain in split after recents restores. - WindowContainerTransaction restoreWCT = new WindowContainerTransaction(); - restoreWCT.reorder(mMainChild.token, true /* toTop */); - restoreWCT.reorder(mSideChild.token, true /* toTop */); - // simulate the restoreWCT being applied: - mMainStage.onTaskAppeared(mMainChild, mock(SurfaceControl.class)); - mSideStage.onTaskAppeared(mSideChild, mock(SurfaceControl.class)); - mStageCoordinator.onRecentsInSplitAnimationFinish(restoreWCT, - mock(SurfaceControl.Transaction.class)); + mStageCoordinator.startAnimation(transition, info, + mock(SurfaceControl.Transaction.class), + mock(SurfaceControl.Transaction.class), + mock(Transitions.TransitionFinishCallback.class)); assertTrue(mStageCoordinator.isSplitScreenVisible()); } diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index 23f87abaffed..f86b9af25933 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -1566,7 +1566,7 @@ public class ExifInterface { FileInputStream in = null; try { in = new FileInputStream(fileDescriptor); - loadAttributes(in); + loadAttributes(in, fileDescriptor); } finally { closeQuietly(in); if (isFdDuped) { @@ -1637,7 +1637,7 @@ public class ExifInterface { mSeekableFileDescriptor = null; } } - loadAttributes(inputStream); + loadAttributes(inputStream, null); } /** @@ -1963,7 +1963,7 @@ public class ExifInterface { * This function decides which parser to read the image data according to the given input stream * type and the content of the input stream. */ - private void loadAttributes(@NonNull InputStream in) { + private void loadAttributes(@NonNull InputStream in, @Nullable FileDescriptor fd) { if (in == null) { throw new NullPointerException("inputstream shouldn't be null"); } @@ -1993,7 +1993,7 @@ public class ExifInterface { break; } case IMAGE_TYPE_HEIF: { - getHeifAttributes(inputStream); + getHeifAttributes(inputStream, fd); break; } case IMAGE_TYPE_ORF: { @@ -2580,7 +2580,7 @@ public class ExifInterface { } else if (isSeekableFD(in.getFD())) { mSeekableFileDescriptor = in.getFD(); } - loadAttributes(in); + loadAttributes(in, null); } finally { closeQuietly(in); if (modernFd != null) { @@ -3068,59 +3068,66 @@ public class ExifInterface { } } - private void getHeifAttributes(ByteOrderedDataInputStream in) throws IOException { + private void getHeifAttributes(ByteOrderedDataInputStream in, @Nullable FileDescriptor fd) + throws IOException { MediaMetadataRetriever retriever = new MediaMetadataRetriever(); try { - retriever.setDataSource(new MediaDataSource() { - long mPosition; + if (fd != null) { + retriever.setDataSource(fd); + } else { + retriever.setDataSource(new MediaDataSource() { + long mPosition; - @Override - public void close() throws IOException {} + @Override + public void close() throws IOException {} - @Override - public int readAt(long position, byte[] buffer, int offset, int size) - throws IOException { - if (size == 0) { - return 0; - } - if (position < 0) { - return -1; - } - try { - if (mPosition != position) { - // We don't allow seek to positions after the available bytes, - // the input stream won't be able to seek back then. - // However, if we hit an exception before (mPosition set to -1), - // let it try the seek in hope it might recover. - if (mPosition >= 0 && position >= mPosition + in.available()) { - return -1; - } - in.seek(position); - mPosition = position; + @Override + public int readAt(long position, byte[] buffer, int offset, int size) + throws IOException { + if (size == 0) { + return 0; } - - // If the read will cause us to go over the available bytes, - // reduce the size so that we stay in the available range. - // Otherwise the input stream may not be able to seek back. - if (size > in.available()) { - size = in.available(); + if (position < 0) { + return -1; } + try { + if (mPosition != position) { + // We don't allow seek to positions after the available bytes, + // the input stream won't be able to seek back then. + // However, if we hit an exception before (mPosition set to -1), + // let it try the seek in hope it might recover. + if (mPosition >= 0 && position >= mPosition + in.available()) { + return -1; + } + in.seek(position); + mPosition = position; + } - int bytesRead = in.read(buffer, offset, size); - if (bytesRead >= 0) { - mPosition += bytesRead; - return bytesRead; + // If the read will cause us to go over the available bytes, + // reduce the size so that we stay in the available range. + // Otherwise the input stream may not be able to seek back. + if (size > in.available()) { + size = in.available(); + } + + int bytesRead = in.read(buffer, offset, size); + if (bytesRead >= 0) { + mPosition += bytesRead; + return bytesRead; + } + } catch (IOException e) { + // absorb the exception and fall through to the 'failed read' path below } - } catch (IOException e) {} - mPosition = -1; // need to seek on next read - return -1; - } + mPosition = -1; // need to seek on next read + return -1; + } - @Override - public long getSize() throws IOException { - return -1; - } - }); + @Override + public long getSize() throws IOException { + return -1; + } + }); + } String exifOffsetStr = retriever.extractMetadata( MediaMetadataRetriever.METADATA_KEY_EXIF_OFFSET); diff --git a/packages/CarrierDefaultApp/res/values/strings.xml b/packages/CarrierDefaultApp/res/values/strings.xml index e91d35bf6979..f4e89a147b9f 100644 --- a/packages/CarrierDefaultApp/res/values/strings.xml +++ b/packages/CarrierDefaultApp/res/values/strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <string name="app_name">CarrierDefaultApp</string> + <string name="app_name">Carrier Communications</string> <string name="android_system_label">Mobile Carrier</string> <string name="portal_notification_id">Mobile data has run out</string> <string name="no_data_notification_id">Your mobile data has been deactivated</string> @@ -17,9 +17,9 @@ <!-- Telephony notification channel name for performance boost notifications. --> <string name="performance_boost_notification_channel">Performance boost</string> <!-- Notification title text for the performance boost notification. --> - <string name="performance_boost_notification_title">Improve your app experience</string> + <string name="performance_boost_notification_title">5G options from your carrier</string> <!-- Notification detail text for the performance boost notification. --> - <string name="performance_boost_notification_detail">Tap to visit %s\'s website and learn more</string> + <string name="performance_boost_notification_detail">Visit %s\'s website to see options for your app experience</string> <!-- Notification button text to cancel the performance boost notification. --> <string name="performance_boost_notification_button_not_now">Not now</string> <!-- Notification button text to manage the performance boost notification. --> diff --git a/packages/SystemUI/res/drawable/ic_important_outline.xml b/packages/SystemUI/res/drawable/ic_important_outline.xml index 7a628bb65433..642582c755ac 100644 --- a/packages/SystemUI/res/drawable/ic_important_outline.xml +++ b/packages/SystemUI/res/drawable/ic_important_outline.xml @@ -20,6 +20,7 @@ android:height="24dp" android:viewportWidth="24.0" android:viewportHeight="24.0" + android:autoMirrored="true" android:tint="?android:attr/colorControlNormal"> <path android:fillColor="@android:color/white" diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java index 0e5f8c1c7a26..553453d7f79d 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java @@ -18,13 +18,9 @@ package com.android.keyguard; import android.content.Context; import android.content.res.TypedArray; -import android.os.Handler; -import android.os.Looper; -import android.os.SystemClock; import android.text.TextUtils; import android.util.AttributeSet; import android.util.TypedValue; -import android.view.View; import android.view.ViewGroup; import android.widget.TextView; @@ -33,22 +29,10 @@ import androidx.annotation.Nullable; import com.android.internal.policy.SystemBarUtils; import com.android.systemui.R; -import java.lang.ref.WeakReference; - /*** * Manages a number of views inside of the given layout. See below for a list of widgets. */ public abstract class KeyguardMessageArea extends TextView implements SecurityMessageDisplay { - /** Handler token posted with accessibility announcement runnables. */ - private static final Object ANNOUNCE_TOKEN = new Object(); - - /** - * Delay before speaking an accessibility announcement. Used to prevent - * lift-to-type from interrupting itself. - */ - private static final long ANNOUNCEMENT_DELAY = 250; - - private final Handler mHandler; private CharSequence mMessage; private boolean mIsVisible; @@ -65,7 +49,6 @@ public abstract class KeyguardMessageArea extends TextView implements SecurityMe super(context, attrs); setLayerType(LAYER_TYPE_HARDWARE, null); // work around nested unclipped SaveLayer bug - mHandler = new Handler(Looper.myLooper()); onThemeChanged(); } @@ -127,9 +110,6 @@ public abstract class KeyguardMessageArea extends TextView implements SecurityMe private void securityMessageChanged(CharSequence message) { mMessage = message; update(); - mHandler.removeCallbacksAndMessages(ANNOUNCE_TOKEN); - mHandler.postAtTime(new AnnounceRunnable(this, getText()), ANNOUNCE_TOKEN, - (SystemClock.uptimeMillis() + ANNOUNCEMENT_DELAY)); } private void clearMessage() { @@ -156,25 +136,4 @@ public abstract class KeyguardMessageArea extends TextView implements SecurityMe /** Set the text color */ protected abstract void updateTextColor(); - - /** - * Runnable used to delay accessibility announcements. - */ - private static class AnnounceRunnable implements Runnable { - private final WeakReference<View> mHost; - private final CharSequence mTextToAnnounce; - - AnnounceRunnable(View host, CharSequence textToAnnounce) { - mHost = new WeakReference<View>(host); - mTextToAnnounce = textToAnnounce; - } - - @Override - public void run() { - final View host = mHost.get(); - if (host != null) { - host.announceForAccessibility(mTextToAnnounce); - } - } - } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java index 6a9216218d07..c1896fc641e0 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java @@ -18,11 +18,17 @@ package com.android.keyguard; import android.content.res.ColorStateList; import android.content.res.Configuration; +import android.text.TextUtils; +import android.view.View; + +import androidx.annotation.VisibleForTesting; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.util.ViewController; +import java.lang.ref.WeakReference; + import javax.inject.Inject; /** @@ -31,8 +37,14 @@ import javax.inject.Inject; */ public class KeyguardMessageAreaController<T extends KeyguardMessageArea> extends ViewController<T> { + /** + * Delay before speaking an accessibility announcement. Used to prevent + * lift-to-type from interrupting itself. + */ + private static final long ANNOUNCEMENT_DELAY = 250; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final ConfigurationController mConfigurationController; + private final AnnounceRunnable mAnnounceRunnable; private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() { public void onFinishedGoingToSleep(int why) { @@ -68,6 +80,7 @@ public class KeyguardMessageAreaController<T extends KeyguardMessageArea> mKeyguardUpdateMonitor = keyguardUpdateMonitor; mConfigurationController = configurationController; + mAnnounceRunnable = new AnnounceRunnable(mView); } @Override @@ -100,6 +113,12 @@ public class KeyguardMessageAreaController<T extends KeyguardMessageArea> */ public void setMessage(CharSequence s, boolean animate) { mView.setMessage(s, animate); + CharSequence msg = mView.getText(); + if (!TextUtils.isEmpty(msg)) { + mView.removeCallbacks(mAnnounceRunnable); + mAnnounceRunnable.setTextToAnnounce(msg); + mView.postDelayed(mAnnounceRunnable, ANNOUNCEMENT_DELAY); + } } public void setMessage(int resId) { @@ -134,4 +153,30 @@ public class KeyguardMessageAreaController<T extends KeyguardMessageArea> view, mKeyguardUpdateMonitor, mConfigurationController); } } + + /** + * Runnable used to delay accessibility announcements. + */ + @VisibleForTesting + public static class AnnounceRunnable implements Runnable { + private final WeakReference<View> mHost; + private CharSequence mTextToAnnounce; + + AnnounceRunnable(View host) { + mHost = new WeakReference<>(host); + } + + /** Sets the text to announce. */ + public void setTextToAnnounce(CharSequence textToAnnounce) { + mTextToAnnounce = textToAnnounce; + } + + @Override + public void run() { + final View host = mHost.get(); + if (host != null) { + host.announceForAccessibility(mTextToAnnounce); + } + } + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index 98866c694526..7255383049da 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -1066,23 +1066,28 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } private void reloadColors() { - reinflateViewFlipper(); - mView.reloadColors(); + reinflateViewFlipper(() -> mView.reloadColors()); } /** Handles density or font scale changes. */ private void onDensityOrFontScaleChanged() { - reinflateViewFlipper(); - mView.onDensityOrFontScaleChanged(); + reinflateViewFlipper(() -> mView.onDensityOrFontScaleChanged()); } /** * Reinflate the view flipper child view. */ - public void reinflateViewFlipper() { + public void reinflateViewFlipper( + KeyguardSecurityViewFlipperController.OnViewInflatedListener onViewInflatedListener) { mSecurityViewFlipperController.clearViews(); - mSecurityViewFlipperController.getSecurityView(mCurrentSecurityMode, - mKeyguardSecurityCallback); + if (mFeatureFlags.isEnabled(Flags.ASYNC_INFLATE_BOUNCER)) { + mSecurityViewFlipperController.asynchronouslyInflateView(mCurrentSecurityMode, + mKeyguardSecurityCallback, onViewInflatedListener); + } else { + mSecurityViewFlipperController.getSecurityView(mCurrentSecurityMode, + mKeyguardSecurityCallback); + onViewInflatedListener.onViewInflated(); + } } /** diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java index 39b567fd21b9..68e1dd7d8eab 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java @@ -19,11 +19,16 @@ package com.android.keyguard; import android.util.Log; import android.view.LayoutInflater; +import androidx.annotation.Nullable; +import androidx.asynclayoutinflater.view.AsyncLayoutInflater; + import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.KeyguardInputViewController.Factory; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.keyguard.dagger.KeyguardBouncerScope; import com.android.systemui.R; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.util.ViewController; import java.util.ArrayList; @@ -44,18 +49,24 @@ public class KeyguardSecurityViewFlipperController private final List<KeyguardInputViewController<KeyguardInputView>> mChildren = new ArrayList<>(); private final LayoutInflater mLayoutInflater; + private final AsyncLayoutInflater mAsyncLayoutInflater; private final EmergencyButtonController.Factory mEmergencyButtonControllerFactory; private final Factory mKeyguardSecurityViewControllerFactory; + private final FeatureFlags mFeatureFlags; @Inject protected KeyguardSecurityViewFlipperController(KeyguardSecurityViewFlipper view, LayoutInflater layoutInflater, + AsyncLayoutInflater asyncLayoutInflater, KeyguardInputViewController.Factory keyguardSecurityViewControllerFactory, - EmergencyButtonController.Factory emergencyButtonControllerFactory) { + EmergencyButtonController.Factory emergencyButtonControllerFactory, + FeatureFlags featureFlags) { super(view); mKeyguardSecurityViewControllerFactory = keyguardSecurityViewControllerFactory; mLayoutInflater = layoutInflater; mEmergencyButtonControllerFactory = emergencyButtonControllerFactory; + mAsyncLayoutInflater = asyncLayoutInflater; + mFeatureFlags = featureFlags; } @Override @@ -92,13 +103,12 @@ public class KeyguardSecurityViewFlipperController } } - if (childController == null + if (!mFeatureFlags.isEnabled(Flags.ASYNC_INFLATE_BOUNCER) && childController == null && securityMode != SecurityMode.None && securityMode != SecurityMode.Invalid) { - int layoutId = getLayoutIdFor(securityMode); KeyguardInputView view = null; if (layoutId != 0) { - if (DEBUG) Log.v(TAG, "inflating id = " + layoutId); + if (DEBUG) Log.v(TAG, "inflating on main thread id = " + layoutId); view = (KeyguardInputView) mLayoutInflater.inflate( layoutId, mView, false); mView.addView(view); @@ -119,6 +129,36 @@ public class KeyguardSecurityViewFlipperController return childController; } + /** + * Asynchronously inflate view and then add it to view flipper on the main thread when complete. + * + * OnInflateFinishedListener will be called on the main thread. + * + * @param securityMode + * @param keyguardSecurityCallback + */ + public void asynchronouslyInflateView(SecurityMode securityMode, + KeyguardSecurityCallback keyguardSecurityCallback, + @Nullable OnViewInflatedListener onViewInflatedListener) { + int layoutId = getLayoutIdFor(securityMode); + if (layoutId != 0) { + if (DEBUG) Log.v(TAG, "inflating on bg thread id = " + layoutId); + mAsyncLayoutInflater.inflate(layoutId, mView, + (view, resId, parent) -> { + mView.addView(view); + KeyguardInputViewController<KeyguardInputView> childController = + mKeyguardSecurityViewControllerFactory.create( + (KeyguardInputView) view, securityMode, + keyguardSecurityCallback); + childController.init(); + mChildren.add(childController); + if (onViewInflatedListener != null) { + onViewInflatedListener.onViewInflated(); + } + }); + } + } + private int getLayoutIdFor(SecurityMode securityMode) { switch (securityMode) { case Pattern: return R.layout.keyguard_pattern_view; @@ -162,4 +202,10 @@ public class KeyguardSecurityViewFlipperController return 0; } } + + /** Listener to when view has finished inflation. */ + public interface OnViewInflatedListener { + /** Notifies that view has been inflated */ + void onViewInflated(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java index b054c7ee2eaf..0be3bb69d136 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java @@ -98,6 +98,7 @@ import android.view.accessibility.CaptioningManager; import android.view.inputmethod.InputMethodManager; import android.view.textclassifier.TextClassificationManager; +import androidx.asynclayoutinflater.view.AsyncLayoutInflater; import androidx.core.app.NotificationManagerCompat; import com.android.internal.app.IBatteryStats; @@ -395,6 +396,13 @@ public class FrameworkServicesModule { return LayoutInflater.from(context); } + /** */ + @Provides + @Singleton + public AsyncLayoutInflater provideAsyncLayoutInflater(Context context) { + return new AsyncLayoutInflater(context); + } + @Provides static MediaProjectionManager provideMediaProjectionManager(Context context) { return context.getSystemService(MediaProjectionManager.class); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java index 450fa1420408..82be009a7560 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java @@ -176,10 +176,10 @@ public class WorkLockActivity extends Activity { return; } - final Intent credential = getKeyguardManager() + final Intent confirmCredentialIntent = getKeyguardManager() .createConfirmDeviceCredentialIntent(null, null, getTargetUserId(), true /* disallowBiometricsIfPolicyExists */); - if (credential == null) { + if (confirmCredentialIntent == null) { return; } @@ -193,14 +193,18 @@ public class WorkLockActivity extends Activity { PendingIntent.FLAG_IMMUTABLE, options.toBundle()); if (target != null) { - credential.putExtra(Intent.EXTRA_INTENT, target.getIntentSender()); + confirmCredentialIntent.putExtra(Intent.EXTRA_INTENT, target.getIntentSender()); } + // WorkLockActivity is started as a task overlay, so unless credential confirmation is also + // started as an overlay, it won't be visible. final ActivityOptions launchOptions = ActivityOptions.makeBasic(); launchOptions.setLaunchTaskId(getTaskId()); launchOptions.setTaskOverlay(true /* taskOverlay */, true /* canResume */); + // Propagate it in case more than one activity is launched. + confirmCredentialIntent.putExtra(KeyguardManager.EXTRA_FORCE_TASK_OVERLAY, true); - startActivityForResult(credential, REQUEST_CODE_CONFIRM_CREDENTIALS, + startActivityForResult(confirmCredentialIntent, REQUEST_CODE_CONFIRM_CREDENTIALS, launchOptions.toBundle()); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt index d7167845419b..5fcf1052d949 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt @@ -34,7 +34,6 @@ import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransition import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.ActivityStarter import kotlinx.coroutines.awaitCancellation -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch @@ -112,16 +111,18 @@ object KeyguardBouncerViewBinder { launch { viewModel.show.collect { // Reset Security Container entirely. - view.visibility = View.VISIBLE - securityContainerController.onBouncerVisibilityChanged( - /* isVisible= */ true - ) - securityContainerController.reinflateViewFlipper() - securityContainerController.showPrimarySecurityScreen( - /* turningOff= */ false - ) - securityContainerController.appear() - securityContainerController.onResume(KeyguardSecurityView.SCREEN_ON) + securityContainerController.reinflateViewFlipper { + // Reset Security Container entirely. + view.visibility = View.VISIBLE + securityContainerController.onBouncerVisibilityChanged( + /* isVisible= */ true + ) + securityContainerController.showPrimarySecurityScreen( + /* turningOff= */ false + ) + securityContainerController.appear() + securityContainerController.onResume(KeyguardSecurityView.SCREEN_ON) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index e2f31e8af196..289908136e7e 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -16,6 +16,7 @@ package com.android.systemui.shade; +import static com.android.systemui.flags.Flags.TRACKPAD_GESTURE_BACK; import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; import android.app.StatusBarManager; @@ -39,6 +40,7 @@ import com.android.systemui.R; import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dock.DockManager; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; @@ -88,10 +90,12 @@ public class NotificationShadeWindowViewController { private final NotificationInsetsController mNotificationInsetsController; private final AlternateBouncerInteractor mAlternateBouncerInteractor; private final UdfpsOverlayInteractor mUdfpsOverlayInteractor; + private final boolean mIsTrackpadGestureBackEnabled; private GestureDetector mPulsingWakeupGestureHandler; private View mBrightnessMirror; private boolean mTouchActive; private boolean mTouchCancelled; + private MotionEvent mDownEvent; private boolean mExpandAnimationRunning; private NotificationStackScrollLayout mStackScrollLayout; private PhoneStatusBarViewController mStatusBarViewController; @@ -137,8 +141,8 @@ public class NotificationShadeWindowViewController { AlternateBouncerInteractor alternateBouncerInteractor, UdfpsOverlayInteractor udfpsOverlayInteractor, KeyguardTransitionInteractor keyguardTransitionInteractor, - PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel - ) { + PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel, + FeatureFlags featureFlags) { mLockscreenShadeTransitionController = transitionController; mFalsingCollector = falsingCollector; mStatusBarStateController = statusBarStateController; @@ -159,6 +163,7 @@ public class NotificationShadeWindowViewController { mNotificationInsetsController = notificationInsetsController; mAlternateBouncerInteractor = alternateBouncerInteractor; mUdfpsOverlayInteractor = udfpsOverlayInteractor; + mIsTrackpadGestureBackEnabled = featureFlags.isEnabled(TRACKPAD_GESTURE_BACK); // This view is not part of the newly inflated expanded status bar. mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container); @@ -219,9 +224,11 @@ public class NotificationShadeWindowViewController { if (isDown) { mTouchActive = true; mTouchCancelled = false; + mDownEvent = ev; } else if (ev.getActionMasked() == MotionEvent.ACTION_UP || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) { mTouchActive = false; + mDownEvent = null; } if (mTouchCancelled || mExpandAnimationRunning) { return false; @@ -447,9 +454,17 @@ public class NotificationShadeWindowViewController { public void cancelCurrentTouch() { if (mTouchActive) { final long now = SystemClock.uptimeMillis(); - MotionEvent event = MotionEvent.obtain(now, now, - MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0); - event.setSource(InputDevice.SOURCE_TOUCHSCREEN); + final MotionEvent event; + if (mIsTrackpadGestureBackEnabled) { + event = MotionEvent.obtain(mDownEvent); + event.setDownTime(now); + event.setAction(MotionEvent.ACTION_CANCEL); + event.setLocation(0.0f, 0.0f); + } else { + event = MotionEvent.obtain(now, now, + MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0); + event.setSource(InputDevice.SOURCE_TOUCHSCREEN); + } mView.dispatchTouchEvent(event); event.recycle(); mTouchCancelled = true; diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java index 0e837d2976ba..a35e5b59f765 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java @@ -18,12 +18,15 @@ package com.android.keyguard; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -37,6 +40,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest +@TestableLooper.RunWithLooper @RunWith(AndroidTestingRunner.class) public class KeyguardMessageAreaControllerTest extends SysuiTestCase { @Mock @@ -45,14 +49,14 @@ public class KeyguardMessageAreaControllerTest extends SysuiTestCase { private KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Mock private KeyguardMessageArea mKeyguardMessageArea; - private KeyguardMessageAreaController mMessageAreaController; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mMessageAreaController = new KeyguardMessageAreaController.Factory( - mKeyguardUpdateMonitor, mConfigurationController).create(mKeyguardMessageArea); + mKeyguardUpdateMonitor, mConfigurationController).create( + mKeyguardMessageArea); } @Test @@ -89,6 +93,19 @@ public class KeyguardMessageAreaControllerTest extends SysuiTestCase { } @Test + public void testSetMessage_AnnounceForAccessibility() { + ArgumentCaptor<Runnable> argumentCaptor = ArgumentCaptor.forClass(Runnable.class); + when(mKeyguardMessageArea.getText()).thenReturn("abc"); + mMessageAreaController.setMessage("abc"); + + verify(mKeyguardMessageArea).setMessage("abc", /* animate= */ true); + verify(mKeyguardMessageArea).removeCallbacks(any(Runnable.class)); + verify(mKeyguardMessageArea).postDelayed(argumentCaptor.capture(), anyLong()); + argumentCaptor.getValue().run(); + verify(mKeyguardMessageArea).announceForAccessibility("abc"); + } + + @Test public void testSetBouncerVisible() { mMessageAreaController.setIsVisible(true); verify(mKeyguardMessageArea).setIsVisible(true); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java index 38d3a3eec606..f966eb33fc14 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java @@ -66,6 +66,7 @@ import com.android.systemui.biometrics.SideFpsUiRequestSource; import com.android.systemui.classifier.FalsingA11yDelegate; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; @@ -619,13 +620,26 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { @Test public void testReinflateViewFlipper() { - mKeyguardSecurityContainerController.reinflateViewFlipper(); + mKeyguardSecurityContainerController.reinflateViewFlipper(() -> {}); verify(mKeyguardSecurityViewFlipperController).clearViews(); verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class), any(KeyguardSecurityCallback.class)); } @Test + public void testReinflateViewFlipper_asyncBouncerFlagOn() { + when(mFeatureFlags.isEnabled(Flags.ASYNC_INFLATE_BOUNCER)).thenReturn(true); + KeyguardSecurityViewFlipperController.OnViewInflatedListener onViewInflatedListener = + () -> { + }; + mKeyguardSecurityContainerController.reinflateViewFlipper(onViewInflatedListener); + verify(mKeyguardSecurityViewFlipperController).clearViews(); + verify(mKeyguardSecurityViewFlipperController).asynchronouslyInflateView( + any(SecurityMode.class), + any(KeyguardSecurityCallback.class), eq(onViewInflatedListener)); + } + + @Test public void testSideFpsControllerShow() { mKeyguardSecurityContainerController.updateSideFpsVisibility(/* isVisible= */ true); verify(mSideFpsController).show( diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java index 1614b577a6cc..afb54d2df49f 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java @@ -31,10 +31,12 @@ import android.view.LayoutInflater; import android.view.ViewGroup; import android.view.WindowInsetsController; +import androidx.asynclayoutinflater.view.AsyncLayoutInflater; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.systemui.SysuiTestCase; +import com.android.systemui.flags.FeatureFlags; import org.junit.Before; import org.junit.Rule; @@ -57,6 +59,8 @@ public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase { @Mock private LayoutInflater mLayoutInflater; @Mock + private AsyncLayoutInflater mAsyncLayoutInflater; + @Mock private KeyguardInputViewController.Factory mKeyguardSecurityViewControllerFactory; @Mock private EmergencyButtonController.Factory mEmergencyButtonControllerFactory; @@ -70,6 +74,8 @@ public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase { private WindowInsetsController mWindowInsetsController; @Mock private KeyguardSecurityCallback mKeyguardSecurityCallback; + @Mock + private FeatureFlags mFeatureFlags; private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController; @@ -82,10 +88,11 @@ public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase { when(mView.getWindowInsetsController()).thenReturn(mWindowInsetsController); when(mEmergencyButtonControllerFactory.create(any(EmergencyButton.class))) .thenReturn(mEmergencyButtonController); + when(mView.getContext()).thenReturn(getContext()); mKeyguardSecurityViewFlipperController = new KeyguardSecurityViewFlipperController(mView, - mLayoutInflater, mKeyguardSecurityViewControllerFactory, - mEmergencyButtonControllerFactory); + mLayoutInflater, mAsyncLayoutInflater, mKeyguardSecurityViewControllerFactory, + mEmergencyButtonControllerFactory, mFeatureFlags); } @Test @@ -108,6 +115,14 @@ public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase { } @Test + public void asynchronouslyInflateView() { + mKeyguardSecurityViewFlipperController.asynchronouslyInflateView(SecurityMode.PIN, + mKeyguardSecurityCallback, null); + verify(mAsyncLayoutInflater).inflate(anyInt(), eq(mView), any( + AsyncLayoutInflater.OnInflateFinishedListener.class)); + } + + @Test public void onDensityOrFontScaleChanged() { mKeyguardSecurityViewFlipperController.clearViews(); verify(mView).removeAllViews(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index 51492eb8e532..0dc2d1700f15 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -29,6 +29,8 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor import com.android.systemui.classifier.FalsingCollectorFake import com.android.systemui.dock.DockManager +import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.keyguard.KeyguardUnlockAnimationController import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor @@ -112,6 +114,9 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { .thenReturn(keyguardSecurityContainerController) whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition) .thenReturn(emptyFlow<TransitionStep>()) + + val featureFlags = FakeFeatureFlags(); + featureFlags.set(Flags.TRACKPAD_GESTURE_BACK, false) underTest = NotificationShadeWindowViewController( lockscreenShadeTransitionController, @@ -138,6 +143,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { udfpsOverlayInteractor, keyguardTransitionInteractor, primaryBouncerToGoneTransitionViewModel, + featureFlags, ) underTest.setupExpandedStatusBar() diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java index 2f528a86cc2d..2797440ce1f1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java @@ -43,6 +43,8 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.dock.DockManager; +import com.android.systemui.flags.FakeFeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; @@ -132,6 +134,8 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { when(mKeyguardTransitionInteractor.getLockscreenToDreamingTransition()) .thenReturn(emptyFlow()); + FakeFeatureFlags featureFlags = new FakeFeatureFlags(); + featureFlags.set(Flags.TRACKPAD_GESTURE_BACK, false); mController = new NotificationShadeWindowViewController( mLockscreenShadeTransitionController, new FalsingCollectorFake(), @@ -156,7 +160,8 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { mAlternateBouncerInteractor, mUdfpsOverlayInteractor, mKeyguardTransitionInteractor, - mPrimaryBouncerToGoneTransitionViewModel + mPrimaryBouncerToGoneTransitionViewModel, + featureFlags ); mController.setupExpandedStatusBar(); mController.setDragDownHelper(mDragDownHelper); diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java index bac9253d5b93..8e93c1b8e2ac 100644 --- a/services/core/java/com/android/server/am/ProcessStatsService.java +++ b/services/core/java/com/android/server/am/ProcessStatsService.java @@ -69,7 +69,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { // define the encoding of that data in an integer. static final int MAX_HISTORIC_STATES = 8; // Maximum number of historic states we will keep. - static final String STATE_FILE_PREFIX = "state-"; // Prefix to use for state filenames. + static final String STATE_FILE_PREFIX = "state-v2-"; // Prefix to use for state filenames. static final String STATE_FILE_SUFFIX = ".bin"; // Suffix to use for state filenames. static final String STATE_FILE_CHECKIN_SUFFIX = ".ci"; // State files that have checked in. static long WRITE_PERIOD = 30*60*1000; // Write file every 30 minutes or so. @@ -462,6 +462,10 @@ public final class ProcessStatsService extends IProcessStats.Stub { File file = files[i]; String fileStr = file.getPath(); if (DEBUG) Slog.d(TAG, "Collecting: " + fileStr); + if (!file.getName().startsWith(STATE_FILE_PREFIX)) { + if (DEBUG) Slog.d(TAG, "Skipping: mismatching prefix"); + continue; + } if (!inclCheckedIn && fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX)) { if (DEBUG) Slog.d(TAG, "Skipping: already checked in"); continue; @@ -478,6 +482,14 @@ public final class ProcessStatsService extends IProcessStats.Stub { @GuardedBy("mFileLock") private void trimHistoricStatesWriteLF() { + File[] files = mBaseDir.listFiles(); + if (files != null) { + for (int i = 0; i < files.length; i++) { + if (!files[i].getName().startsWith(STATE_FILE_PREFIX)) { + files[i].delete(); + } + } + } ArrayList<String> filesArray = getCommittedFilesLF(MAX_HISTORIC_STATES, false, true); if (filesArray == null) { return; diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java index 5e0a18039152..8417049bf297 100644 --- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java +++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java @@ -62,7 +62,7 @@ import java.util.concurrent.TimeUnit; public class ValidateNotificationPeople implements NotificationSignalExtractor { // Using a shorter log tag since setprop has a limit of 32chars on variable name. private static final String TAG = "ValidateNoPeople"; - private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);; + private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final boolean ENABLE_PEOPLE_VALIDATOR = true; @@ -105,12 +105,13 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { private int mEvictionCount; private NotificationUsageStats mUsageStats; + @Override public void initialize(Context context, NotificationUsageStats usageStats) { if (DEBUG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + "."); mUserToContextMap = new ArrayMap<>(); mBaseContext = context; mUsageStats = usageStats; - mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE); + mPeopleCache = new LruCache<>(PEOPLE_CACHE_SIZE); mEnabled = ENABLE_PEOPLE_VALIDATOR && 1 == Settings.Global.getInt( mBaseContext.getContentResolver(), SETTING_ENABLE_PEOPLE_VALIDATOR, 1); if (mEnabled) { @@ -134,7 +135,7 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { // For tests: just do the setting of various local variables without actually doing work @VisibleForTesting protected void initForTests(Context context, NotificationUsageStats usageStats, - LruCache peopleCache) { + LruCache<String, LookupResult> peopleCache) { mUserToContextMap = new ArrayMap<>(); mBaseContext = context; mUsageStats = usageStats; @@ -142,6 +143,7 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { mEnabled = true; } + @Override public RankingReconsideration process(NotificationRecord record) { if (!mEnabled) { if (VERBOSE) Slog.i(TAG, "disabled"); @@ -272,7 +274,7 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { } if (VERBOSE) Slog.i(TAG, "Validating: " + key + " for " + context.getUserId()); - final LinkedList<String> pendingLookups = new LinkedList<String>(); + final LinkedList<String> pendingLookups = new LinkedList<>(); int personIdx = 0; for (String handle : people) { if (TextUtils.isEmpty(handle)) continue; @@ -320,7 +322,6 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { return Integer.toString(userId) + ":" + handle; } - // VisibleForTesting public static String[] getExtraPeople(Bundle extras) { String[] peopleList = getExtraPeopleForKey(extras, Notification.EXTRA_PEOPLE_LIST); String[] legacyPeople = getExtraPeopleForKey(extras, Notification.EXTRA_PEOPLE); @@ -417,101 +418,6 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { return null; } - private LookupResult resolvePhoneContact(Context context, final String number) { - Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, - Uri.encode(number)); - return searchContacts(context, phoneUri); - } - - private LookupResult resolveEmailContact(Context context, final String email) { - Uri numberUri = Uri.withAppendedPath( - ContactsContract.CommonDataKinds.Email.CONTENT_LOOKUP_URI, - Uri.encode(email)); - return searchContacts(context, numberUri); - } - - @VisibleForTesting - LookupResult searchContacts(Context context, Uri lookupUri) { - LookupResult lookupResult = new LookupResult(); - final Uri corpLookupUri = - ContactsContract.Contacts.createCorpLookupUriFromEnterpriseLookupUri(lookupUri); - if (corpLookupUri == null) { - addContacts(lookupResult, context, lookupUri); - } else { - addWorkContacts(lookupResult, context, corpLookupUri); - } - return lookupResult; - } - - @VisibleForTesting - // Performs a contacts search using searchContacts, and then follows up by looking up - // any phone numbers associated with the resulting contact information and merge those - // into the lookup result as well. Will have no additional effect if the contact does - // not have any phone numbers. - LookupResult searchContactsAndLookupNumbers(Context context, Uri lookupUri) { - LookupResult lookupResult = searchContacts(context, lookupUri); - String phoneLookupKey = lookupResult.getPhoneLookupKey(); - if (phoneLookupKey != null) { - String selection = Contacts.LOOKUP_KEY + " = ?"; - String[] selectionArgs = new String[] { phoneLookupKey }; - try (Cursor cursor = context.getContentResolver().query( - ContactsContract.CommonDataKinds.Phone.CONTENT_URI, PHONE_LOOKUP_PROJECTION, - selection, selectionArgs, /* sortOrder= */ null)) { - if (cursor == null) { - Slog.w(TAG, "Cursor is null when querying contact phone number."); - return lookupResult; - } - - while (cursor.moveToNext()) { - lookupResult.mergePhoneNumber(cursor); - } - } catch (Throwable t) { - Slog.w(TAG, "Problem getting content resolver or querying phone numbers.", t); - } - } - return lookupResult; - } - - private void addWorkContacts(LookupResult lookupResult, Context context, Uri corpLookupUri) { - final int workUserId = findWorkUserId(context); - if (workUserId == -1) { - Slog.w(TAG, "Work profile user ID not found for work contact: " + corpLookupUri); - return; - } - final Uri corpLookupUriWithUserId = - ContentProvider.maybeAddUserId(corpLookupUri, workUserId); - addContacts(lookupResult, context, corpLookupUriWithUserId); - } - - /** Returns the user ID of the managed profile or -1 if none is found. */ - private int findWorkUserId(Context context) { - final UserManager userManager = context.getSystemService(UserManager.class); - final int[] profileIds = - userManager.getProfileIds(context.getUserId(), /* enabledOnly= */ true); - for (int profileId : profileIds) { - if (userManager.isManagedProfile(profileId)) { - return profileId; - } - } - return -1; - } - - /** Modifies the given lookup result to add contacts found at the given URI. */ - private void addContacts(LookupResult lookupResult, Context context, Uri uri) { - try (Cursor c = context.getContentResolver().query( - uri, LOOKUP_PROJECTION, null, null, null)) { - if (c == null) { - Slog.w(TAG, "Null cursor from contacts query."); - return; - } - while (c.moveToNext()) { - lookupResult.mergeContact(c); - } - } catch (Throwable t) { - Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t); - } - } - @VisibleForTesting protected static class LookupResult { private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000; // 1hr @@ -619,19 +525,18 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { } } - private class PeopleRankingReconsideration extends RankingReconsideration { + @VisibleForTesting + class PeopleRankingReconsideration extends RankingReconsideration { private final LinkedList<String> mPendingLookups; private final Context mContext; - // Amount of time to wait for a result from the contacts db before rechecking affinity. - private static final long LOOKUP_TIME = 1000; private float mContactAffinity = NONE; private ArraySet<String> mPhoneNumbers = null; private NotificationRecord mRecord; private PeopleRankingReconsideration(Context context, String key, LinkedList<String> pendingLookups) { - super(key, LOOKUP_TIME); + super(key); mContext = context; mPendingLookups = pendingLookups; } @@ -642,7 +547,7 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { long timeStartMs = System.currentTimeMillis(); for (final String handle: mPendingLookups) { final String cacheKey = getCacheKey(mContext.getUserId(), handle); - LookupResult lookupResult = null; + LookupResult lookupResult; boolean cacheHit = false; synchronized (mPeopleCache) { lookupResult = mPeopleCache.get(cacheKey); @@ -703,6 +608,102 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { } } + private static LookupResult resolvePhoneContact(Context context, final String number) { + Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, + Uri.encode(number)); + return searchContacts(context, phoneUri); + } + + private static LookupResult resolveEmailContact(Context context, final String email) { + Uri numberUri = Uri.withAppendedPath( + ContactsContract.CommonDataKinds.Email.CONTENT_LOOKUP_URI, + Uri.encode(email)); + return searchContacts(context, numberUri); + } + + @VisibleForTesting + static LookupResult searchContacts(Context context, Uri lookupUri) { + LookupResult lookupResult = new LookupResult(); + final Uri corpLookupUri = + ContactsContract.Contacts.createCorpLookupUriFromEnterpriseLookupUri(lookupUri); + if (corpLookupUri == null) { + addContacts(lookupResult, context, lookupUri); + } else { + addWorkContacts(lookupResult, context, corpLookupUri); + } + return lookupResult; + } + + @VisibleForTesting + // Performs a contacts search using searchContacts, and then follows up by looking up + // any phone numbers associated with the resulting contact information and merge those + // into the lookup result as well. Will have no additional effect if the contact does + // not have any phone numbers. + static LookupResult searchContactsAndLookupNumbers(Context context, Uri lookupUri) { + LookupResult lookupResult = searchContacts(context, lookupUri); + String phoneLookupKey = lookupResult.getPhoneLookupKey(); + if (phoneLookupKey != null) { + String selection = Contacts.LOOKUP_KEY + " = ?"; + String[] selectionArgs = new String[] { phoneLookupKey }; + try (Cursor cursor = context.getContentResolver().query( + ContactsContract.CommonDataKinds.Phone.CONTENT_URI, PHONE_LOOKUP_PROJECTION, + selection, selectionArgs, /* sortOrder= */ null)) { + if (cursor == null) { + Slog.w(TAG, "Cursor is null when querying contact phone number."); + return lookupResult; + } + + while (cursor.moveToNext()) { + lookupResult.mergePhoneNumber(cursor); + } + } catch (Throwable t) { + Slog.w(TAG, "Problem getting content resolver or querying phone numbers.", t); + } + } + return lookupResult; + } + + private static void addWorkContacts(LookupResult lookupResult, Context context, + Uri corpLookupUri) { + final int workUserId = findWorkUserId(context); + if (workUserId == -1) { + Slog.w(TAG, "Work profile user ID not found for work contact: " + corpLookupUri); + return; + } + final Uri corpLookupUriWithUserId = + ContentProvider.maybeAddUserId(corpLookupUri, workUserId); + addContacts(lookupResult, context, corpLookupUriWithUserId); + } + + /** Returns the user ID of the managed profile or -1 if none is found. */ + private static int findWorkUserId(Context context) { + final UserManager userManager = context.getSystemService(UserManager.class); + final int[] profileIds = + userManager.getProfileIds(context.getUserId(), /* enabledOnly= */ true); + for (int profileId : profileIds) { + if (userManager.isManagedProfile(profileId)) { + return profileId; + } + } + return -1; + } + + /** Modifies the given lookup result to add contacts found at the given URI. */ + private static void addContacts(LookupResult lookupResult, Context context, Uri uri) { + try (Cursor c = context.getContentResolver().query( + uri, LOOKUP_PROJECTION, null, null, null)) { + if (c == null) { + Slog.w(TAG, "Null cursor from contacts query."); + return; + } + while (c.moveToNext()) { + lookupResult.mergeContact(c); + } + } catch (Throwable t) { + Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t); + } + } + @Override public void applyChangesLocked(NotificationRecord operand) { float affinityBound = operand.getContactAffinity(); diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 7fe6c7d5aa93..569999e48dea 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -1134,22 +1134,22 @@ final class InstallPackageHelper { // behavior. if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, "MinInstallableTargetSdk__install_block_enabled", - false)) { + true)) { int minInstallableTargetSdk = DeviceConfig.getInt(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, "MinInstallableTargetSdk__min_installable_target_sdk", - 0); + PackageManagerService.MIN_INSTALLABLE_TARGET_SDK); // Determine if enforcement is in strict mode boolean strictMode = false; if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, "MinInstallableTargetSdk__install_block_strict_mode_enabled", - false)) { + true)) { if (parsedPackage.getTargetSdkVersion() < DeviceConfig.getInt(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, "MinInstallableTargetSdk__strict_mode_target_sdk", - 0)) { + PackageManagerService.MIN_INSTALLABLE_TARGET_SDK)) { strictMode = true; } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index d3ee52c48448..c5d7d075efd1 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -557,6 +557,14 @@ public class PackageManagerService implements PackageSender, TestUtilityService // How many required verifiers can be on the system. private static final int REQUIRED_VERIFIERS_MAX_COUNT = 2; + /** + * Specifies the minimum target SDK version an apk must specify in order to be installed + * on the system. This improves security and privacy by blocking low + * target sdk apps as malware can target older sdk versions to avoid + * the enforcement of new API behavior. + */ + public static final int MIN_INSTALLABLE_TARGET_SDK = Build.VERSION_CODES.M; + // Compilation reasons. // TODO(b/260124949): Clean this up with the legacy dexopt code. public static final int REASON_FIRST_BOOT = 0; diff --git a/services/core/java/com/android/server/pm/UserVisibilityMediator.java b/services/core/java/com/android/server/pm/UserVisibilityMediator.java index b67850ad8143..3710af6771b4 100644 --- a/services/core/java/com/android/server/pm/UserVisibilityMediator.java +++ b/services/core/java/com/android/server/pm/UserVisibilityMediator.java @@ -63,19 +63,39 @@ import java.util.concurrent.CopyOnWriteArrayList; /** * Class responsible for deciding whether a user is visible (or visible for a given display). * - * <p>Currently, it has 2 "modes" (set on constructor), which defines the class behavior (i.e, the + * <p>Currently, it has 3 "modes" (set on constructor), which defines the class behavior (i.e, the * logic that dictates the result of methods such as {@link #isUserVisible(int)} and * {@link #isUserVisible(int, int)}): * * <ul> - * <li>default: this is the most common mode (used by phones, tablets, foldables, automotives with - * just cluster and driver displayes, etc...), where the logic is based solely on the current - * foreground user (and its started profiles) - * <li>{@code MUMD}: mode for "(concurrent) Multiple Users on Multiple Displays", which is used on - * automotives with passenger display. In this mode, users started in background on the secondary - * display are stored in map. + * <li>default (A.K.A {@code SUSD} - Single User on Single Display): this is the most common mode + * (used by phones, tablets, foldables, cars with just cluster and driver displays, etc.), + * where just the current foreground user and its profiles are visible; hence, most methods are + * optimized to just check for the current user / profile. This mode is unit tested by + * {@link com.android.server.pm.UserVisibilityMediatorSUSDTest} and CTS tested by + * {@link android.multiuser.cts.UserVisibilityTest}. + * <li>concurrent users (A.K.A. {@code MUMD} - Multiple Users on Multiple Displays): typically + * used on automotive builds where the car has additional displays for passengers, it allows users + * to be started in the background but visible on these displays; hence, it contains additional + * maps to account for the visibility state. This mode is unit tested by + * {@link com.android.server.pm.UserVisibilityMediatorMUMDTest} and CTS tested by + * {@link android.multiuser.cts.UserVisibilityTest}. + * <li>no driver (A.K.A. {@code MUPAND} - MUltiple PAssengers, No Driver): extension of the + * previous mode and typically used on automotive builds where the car has additional displays for + * passengers but uses a secondary Android system for the back passengers, so all "human" users + * are started in the background (and the current foreground user is the system user), hence the + * "no driver name". This mode is unit tested by + * {@link com.android.server.pm.UserVisibilityMediatorMUPANDTest} and CTS tested by + * {@link android.multiuser.cts.UserVisibilityVisibleBackgroundUsersOnDefaultDisplayTest}. * </ul> * + * <p>When you make changes in this class, you should run at least the 3 unit tests and + * {@link android.multiuser.cts.UserVisibilityTest} (which actually applies for all modes); for + * example, by calling {@code atest UserVisibilityMediatorSUSDTest UserVisibilityMediatorMUMDTest + * UserVisibilityMediatorMUPANDTest UserVisibilityTest}. Ideally, you should run the other 2 CTS + * tests as well (you can emulate these modes using {@code adb} commands; their javadoc provides + * instructions on how to do so). + * * <p>This class is thread safe. */ public final class UserVisibilityMediator implements Dumpable { diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java index 5d038dcab73a..be7d9b63f779 100644 --- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java +++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java @@ -92,6 +92,7 @@ class ActivityRecordInputSink { } else { mInputWindowHandleWrapper.setInputConfigMasked(0, InputConfig.NOT_TOUCHABLE); } + mInputWindowHandleWrapper.setDisplayId(mActivityRecord.getDisplayId()); return mInputWindowHandleWrapper; } diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index c3c87af51b15..8652914d5205 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -135,7 +135,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub */ static final int CONTROLLABLE_CONFIGS = ActivityInfo.CONFIG_WINDOW_CONFIGURATION | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE | ActivityInfo.CONFIG_SCREEN_SIZE - | ActivityInfo.CONFIG_LAYOUT_DIRECTION; + | ActivityInfo.CONFIG_LAYOUT_DIRECTION | ActivityInfo.CONFIG_DENSITY; static final int CONTROLLABLE_WINDOW_CONFIGS = WINDOW_CONFIG_BOUNDS | WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java index d72cfc70fc02..0564a73bf3fc 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java @@ -15,6 +15,8 @@ */ package com.android.server.notification; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -51,6 +53,8 @@ import android.util.LruCache; import androidx.test.runner.AndroidJUnit4; import com.android.server.UiServiceTestCase; +import com.android.server.notification.ValidateNotificationPeople.LookupResult; +import com.android.server.notification.ValidateNotificationPeople.PeopleRankingReconsideration; import org.junit.Test; import org.junit.runner.RunWith; @@ -60,6 +64,7 @@ import org.mockito.stubbing.Answer; import java.util.ArrayList; import java.util.Arrays; +import java.util.concurrent.TimeUnit; @SmallTest @RunWith(AndroidJUnit4.class) @@ -215,7 +220,7 @@ public class ValidateNotificationPeopleTest extends UiServiceTestCase { ContactsContract.Contacts.CONTENT_LOOKUP_URI, ContactsContract.Contacts.ENTERPRISE_CONTACT_LOOKUP_PREFIX + contactId); - new ValidateNotificationPeople().searchContacts(mockContext, lookupUri); + PeopleRankingReconsideration.searchContacts(mockContext, lookupUri); ArgumentCaptor<Uri> queryUri = ArgumentCaptor.forClass(Uri.class); verify(mockContentResolver).query( @@ -242,7 +247,7 @@ public class ValidateNotificationPeopleTest extends UiServiceTestCase { final Uri lookupUri = Uri.withAppendedPath( ContactsContract.Contacts.CONTENT_LOOKUP_URI, String.valueOf(contactId)); - new ValidateNotificationPeople().searchContacts(mockContext, lookupUri); + PeopleRankingReconsideration.searchContacts(mockContext, lookupUri); ArgumentCaptor<Uri> queryUri = ArgumentCaptor.forClass(Uri.class); verify(mockContentResolver).query( @@ -277,7 +282,7 @@ public class ValidateNotificationPeopleTest extends UiServiceTestCase { // call searchContacts and then mergePhoneNumbers, make sure we never actually // query the content resolver for a phone number - new ValidateNotificationPeople().searchContactsAndLookupNumbers(mockContext, lookupUri); + PeopleRankingReconsideration.searchContactsAndLookupNumbers(mockContext, lookupUri); verify(mockContentResolver, never()).query( eq(ContactsContract.CommonDataKinds.Phone.CONTENT_URI), eq(ValidateNotificationPeople.PHONE_LOOKUP_PROJECTION), @@ -320,7 +325,7 @@ public class ValidateNotificationPeopleTest extends UiServiceTestCase { // call searchContacts and then mergePhoneNumbers, and check that we query // once for the - new ValidateNotificationPeople().searchContactsAndLookupNumbers(mockContext, lookupUri); + PeopleRankingReconsideration.searchContactsAndLookupNumbers(mockContext, lookupUri); verify(mockContentResolver, times(1)).query( eq(ContactsContract.CommonDataKinds.Phone.CONTENT_URI), eq(ValidateNotificationPeople.PHONE_LOOKUP_PROJECTION), @@ -339,7 +344,7 @@ public class ValidateNotificationPeopleTest extends UiServiceTestCase { // Create validator with empty cache ValidateNotificationPeople vnp = new ValidateNotificationPeople(); - LruCache cache = new LruCache<String, ValidateNotificationPeople.LookupResult>(5); + LruCache<String, LookupResult> cache = new LruCache<>(5); vnp.initForTests(mockContext, mockNotificationUsageStats, cache); NotificationRecord record = getNotificationRecord(); @@ -366,9 +371,8 @@ public class ValidateNotificationPeopleTest extends UiServiceTestCase { float affinity = 0.7f; // Create a fake LookupResult for the data we'll pass in - LruCache cache = new LruCache<String, ValidateNotificationPeople.LookupResult>(5); - ValidateNotificationPeople.LookupResult lr = - mock(ValidateNotificationPeople.LookupResult.class); + LruCache<String, LookupResult> cache = new LruCache<>(5); + LookupResult lr = mock(LookupResult.class); when(lr.getAffinity()).thenReturn(affinity); when(lr.getPhoneNumbers()).thenReturn(new ArraySet<>(new String[]{lookupTel})); when(lr.isExpired()).thenReturn(false); @@ -392,6 +396,23 @@ public class ValidateNotificationPeopleTest extends UiServiceTestCase { assertTrue(record.getPhoneNumbers().contains(lookupTel)); } + @Test + public void validatePeople_reconsiderationWillNotBeDelayed() { + final Context mockContext = mock(Context.class); + final ContentResolver mockContentResolver = mock(ContentResolver.class); + when(mockContext.getContentResolver()).thenReturn(mockContentResolver); + ValidateNotificationPeople vnp = new ValidateNotificationPeople(); + vnp.initForTests(mockContext, mock(NotificationUsageStats.class), new LruCache<>(5)); + NotificationRecord record = getNotificationRecord(); + String[] callNumber = new String[]{"tel:12345678910"}; + setNotificationPeople(record, callNumber); + + RankingReconsideration rr = vnp.validatePeople(mockContext, record); + + assertThat(rr).isNotNull(); + assertThat(rr.getDelay(TimeUnit.MILLISECONDS)).isEqualTo(0); + } + // Creates a cursor that points to one item of Contacts data with the specified // columns. private Cursor makeMockCursor(int id, String lookupKey, int starred, int hasPhone) { |