Merge "Modify current KCM files to support shift+capslock" into udc-dev
diff --git a/Android.bp b/Android.bp
index bc6cda3..cff863b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -343,6 +343,7 @@
"hardware/interfaces/biometrics/fingerprint/aidl",
"hardware/interfaces/graphics/common/aidl",
"hardware/interfaces/keymaster/aidl",
+ "system/hardware/interfaces/media/aidl",
],
},
dxflags: [
@@ -632,6 +633,7 @@
"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 28d4433..2751b54 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -6657,12 +6657,13 @@
// 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 0ef8e92..09450f5 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -155,6 +155,14 @@
"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 440ee20..e78fb17 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -8657,13 +8657,13 @@
* 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 bf34c1c..a23d7e4 100644
--- a/core/java/android/credentials/CredentialDescription.java
+++ b/core/java/android/credentials/CredentialDescription.java
@@ -42,7 +42,7 @@
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 @@
* 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 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 722dd08..e6b3069 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -125,6 +125,23 @@
"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 @@
}
/**
+ * @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 @@
* 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 @@
*
* </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 @@
* 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 @@
*
* </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 @@
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 @@
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 db83e62..2fa8b87 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -101,15 +101,15 @@
// 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 @@
}
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 @@
mInitialized = false;
mInitializeHandler = new InitializeSessionHandler();
mSessionId = sessionId;
+ mInterfaceLock = cameraDevice.mInterfaceLock;
}
/**
@@ -599,13 +601,14 @@
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 @@
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 c2b3656..9c878c7 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -117,7 +117,7 @@
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 @@
*/
@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 @@
@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 @@
mSupportedRequestKeys = requestKeys;
mSupportedResultKeys = resultKeys;
mCaptureResultsSupported = !resultKeys.isEmpty();
+ mInterfaceLock = cameraDevice.mInterfaceLock;
}
private void initializeRepeatingRequestPipeline() throws RemoteException {
@@ -969,46 +970,56 @@
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 a47783c..6b60442 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -205,6 +205,16 @@
}
/**
+ * 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 800fc97..aec3487 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8324,6 +8324,13 @@
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 24dcb69..fb25e7a6 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -11626,7 +11626,8 @@
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 0672d63..7f99fb7 100644
--- a/core/java/android/window/SurfaceSyncGroup.java
+++ b/core/java/android/window/SurfaceSyncGroup.java
@@ -21,6 +21,7 @@
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 @@
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 a3209f6..fabb089 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -131,6 +131,19 @@
}
/**
+ * 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 bce0d60..f6bcc46 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_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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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] =
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED] =
ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
- PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_ACTIVITY_CLIENT] =
- 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 818a503..fff778c 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_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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 f3ed09a..3ce234b 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 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 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 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 1ec2613..fccb177 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 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 @@
}
@Test
- public void onTouchEvent_startHandwriting_delegate() {
+ public void onTouchEvent_tryAcceptDelegation_delegatorCallbackCreatesInputConnection() {
View delegateView = new View(mContext);
delegateView.setIsHandwritingDelegate(true);
@@ -245,6 +246,29 @@
}
@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 35b3267..6189914 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.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 @@
}
@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
deleted file mode 120000
index 707dfcf..0000000
--- a/data/keyboards/Vendor_004c_Product_0265.idc
+++ /dev/null
@@ -1 +0,0 @@
-Vendor_05ac_Product_0265.idc
\ No newline at end of file
diff --git a/data/keyboards/Vendor_004c_Product_0265.idc b/data/keyboards/Vendor_004c_Product_0265.idc
new file mode 100644
index 0000000..bfea4db
--- /dev/null
+++ b/data/keyboards/Vendor_004c_Product_0265.idc
@@ -0,0 +1,34 @@
+# 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 d72de64..520d188 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 73a7403..31c5e33 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.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_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 @@
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 @@
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 @@
" 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 @@
" 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 @@
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 4b5ad1d..e09c3c9 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 @@
DismissTransition mPendingDismiss = null;
TransitSession mPendingEnter = null;
+ TransitSession mPendingRecent = null;
TransitSession mPendingResize = null;
private IBinder mAnimatingTransition = null;
@@ -259,6 +260,10 @@
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 @@
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 @@
+ " 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 @@
} 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 @@
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 @@
}
/** 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 9d1a7ea..a5546e5 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 @@
}
};
+ 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 @@
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 @@
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 @@
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 @@
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 b9f6a01..2e86448 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 @@
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 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 @@
/** 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 @@
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 @@
} 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 @@
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 @@
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 @@
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 72da108..3c0ef96 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.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.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 @@
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 7a7ac47..ddd3b44 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 @@
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 @@
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 2edec7d..3901dab 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 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 @@
@Test
@UiThreadTest
- public void testEnterRecentsAndCommit() {
+ public void testEnterRecents() {
enterSplit();
ActivityManager.RunningTaskInfo homeTask = new TestRunningTaskInfoBuilder()
@@ -262,60 +261,24 @@
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
- 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)
+ // 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();
-
- // 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);
-
- // 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 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 23f87ab..f86b9af 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -1566,7 +1566,7 @@
FileInputStream in = null;
try {
in = new FileInputStream(fileDescriptor);
- loadAttributes(in);
+ loadAttributes(in, fileDescriptor);
} finally {
closeQuietly(in);
if (isFdDuped) {
@@ -1637,7 +1637,7 @@
mSeekableFileDescriptor = null;
}
}
- loadAttributes(inputStream);
+ loadAttributes(inputStream, null);
}
/**
@@ -1963,7 +1963,7 @@
* 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 @@
break;
}
case IMAGE_TYPE_HEIF: {
- getHeifAttributes(inputStream);
+ getHeifAttributes(inputStream, fd);
break;
}
case IMAGE_TYPE_ORF: {
@@ -2580,7 +2580,7 @@
} else if (isSeekableFD(in.getFD())) {
mSeekableFileDescriptor = in.getFD();
}
- loadAttributes(in);
+ loadAttributes(in, null);
} finally {
closeQuietly(in);
if (modernFd != null) {
@@ -3068,59 +3068,66 @@
}
}
- 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) {
+ @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;
+ }
+
+ // 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
+ }
+ mPosition = -1; // need to seek on next read
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;
- }
- // 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) {}
- 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 e91d35b..f4e89a14 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 7a628bb..642582c 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 0e5f8c1..553453d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
@@ -18,13 +18,9 @@
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 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 @@
super(context, attrs);
setLayerType(LAYER_TYPE_HARDWARE, null); // work around nested unclipped SaveLayer bug
- mHandler = new Handler(Looper.myLooper());
onThemeChanged();
}
@@ -127,9 +110,6 @@
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 @@
/** 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 6a92162..c1896fc 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -18,11 +18,17 @@
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 @@
*/
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 @@
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mConfigurationController = configurationController;
+ mAnnounceRunnable = new AnnounceRunnable(mView);
}
@Override
@@ -100,6 +113,12 @@
*/
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 @@
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 98866c6..7255383 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -1066,23 +1066,28 @@
}
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 39b567f..68e1dd7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
@@ -19,11 +19,16 @@
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 @@
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 @@
}
}
- 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 @@
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 @@
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 b054c7e..0be3bb6 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.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 @@
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 450fa14..82be009 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 @@
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 @@
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 d716784..5fcf105 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.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 @@
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 e2f31e8..2899081 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.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 @@
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 @@
AlternateBouncerInteractor alternateBouncerInteractor,
UdfpsOverlayInteractor udfpsOverlayInteractor,
KeyguardTransitionInteractor keyguardTransitionInteractor,
- PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel
- ) {
+ PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
+ FeatureFlags featureFlags) {
mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector;
mStatusBarStateController = statusBarStateController;
@@ -159,6 +163,7 @@
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 @@
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 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 0e837d2..a35e5b5 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 @@
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.MockitoAnnotations;
@SmallTest
+@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner.class)
public class KeyguardMessageAreaControllerTest extends SysuiTestCase {
@Mock
@@ -45,14 +49,14 @@
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 @@
}
@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 38d3a3e..f966eb3 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.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 @@
@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 1614b57..afb54d2 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.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 @@
@Mock
private LayoutInflater mLayoutInflater;
@Mock
+ private AsyncLayoutInflater mAsyncLayoutInflater;
+ @Mock
private KeyguardInputViewController.Factory mKeyguardSecurityViewControllerFactory;
@Mock
private EmergencyButtonController.Factory mEmergencyButtonControllerFactory;
@@ -70,6 +74,8 @@
private WindowInsetsController mWindowInsetsController;
@Mock
private KeyguardSecurityCallback mKeyguardSecurityCallback;
+ @Mock
+ private FeatureFlags mFeatureFlags;
private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController;
@@ -82,10 +88,11 @@
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 @@
}
@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 51492eb..0dc2d17 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.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 @@
.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 @@
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 2f528a8..2797440 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.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 @@
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 @@
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 bac9253..8e93c1b 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 @@
// 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 @@
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 @@
@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 5e0a180..8417049 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 @@
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 @@
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 @@
// 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 @@
mEnabled = true;
}
+ @Override
public RankingReconsideration process(NotificationRecord record) {
if (!mEnabled) {
if (VERBOSE) Slog.i(TAG, "disabled");
@@ -272,7 +274,7 @@
}
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 @@
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 @@
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 @@
}
}
- 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 @@
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 @@
}
}
+ 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 7fe6c7d..569999e 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 @@
// 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 d3ee52c..c5d7d07 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 @@
// 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 b67850a..3710af6 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 @@
/**
* 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 5d038dc..be7d9b6 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 @@
} 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 c3c87af..8652914 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 @@
*/
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 d72cfc7..0564a73 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 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 java.util.ArrayList;
import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -215,7 +220,7 @@
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 @@
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 @@
// 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 @@
// 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 @@
// 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 @@
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 @@
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) {