Merge "Move overlay click handling back to SystemUI." into main
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 6e51f00..58763a7 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -313,7 +313,6 @@
"libziparchive",
],
static_libs: [
- "libc++fs",
"libidmap2_policies",
"libidmap2_protos",
"libidmap2daidl",
diff --git a/core/api/current.txt b/core/api/current.txt
index 244cad0..bbb3932 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -34055,6 +34055,7 @@
field public static final String DISALLOW_BLUETOOTH_SHARING = "no_bluetooth_sharing";
field public static final String DISALLOW_CAMERA_TOGGLE = "disallow_camera_toggle";
field public static final String DISALLOW_CELLULAR_2G = "no_cellular_2g";
+ field @FlaggedApi("android.nfc.enable_nfc_user_restriction") public static final String DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO = "no_change_near_field_communication_radio";
field public static final String DISALLOW_CHANGE_WIFI_STATE = "no_change_wifi_state";
field public static final String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth";
field public static final String DISALLOW_CONFIG_BRIGHTNESS = "no_config_brightness";
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index f8a8f5d..b21defb 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -172,11 +172,9 @@
* @param virtualDeviceId the device for which to finish the op
* @param superImpl
*/
- default void finishOperation(IBinder clientId, int code, int uid, String packageName,
+ void finishOperation(IBinder clientId, int code, int uid, String packageName,
String attributionTag, int virtualDeviceId, @NonNull HexConsumer<IBinder, Integer,
- Integer, String, String, Integer> superImpl) {
- superImpl.accept(clientId, code, uid, packageName, attributionTag, virtualDeviceId);
- }
+ Integer, String, String, Integer> superImpl);
/**
* Allows overriding finish proxy op.
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 2d78317..73ac263 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -199,3 +199,11 @@
description: "redacts notifications on the lockscreen if they have the 'sensitiveContent' flag"
bug: "343631648"
}
+
+flag {
+ name: "api_rich_ongoing"
+ is_exported: true
+ namespace: "systemui"
+ description: "Guards new android.app.richongoingnotification api"
+ bug: "337261753"
+}
\ No newline at end of file
diff --git a/core/java/android/app/wearable/WearableSensingManager.java b/core/java/android/app/wearable/WearableSensingManager.java
index b2b14ce..d28969d 100644
--- a/core/java/android/app/wearable/WearableSensingManager.java
+++ b/core/java/android/app/wearable/WearableSensingManager.java
@@ -449,7 +449,7 @@
* Consumer<android.service.voice.HotwordAudioStream>}, the system will check whether the {@link
* android.service.voice.VoiceInteractionService} at that time is {@code
* targetVisComponentName}. If not, the system will call {@link
- * WearableSensingService#onActiveHotwordAudioStopRequested()} and will not forward the audio
+ * WearableSensingService#onStopHotwordAudioStream()} and will not forward the audio
* data to the current {@link android.service.voice.HotwordDetectionService} nor {@link
* android.service.voice.VoiceInteractionService}. The system will not send a status code to
* {@code statusConsumer} regarding the {@code targetVisComponentName} check. The caller is
@@ -464,9 +464,9 @@
* continue to use the previous consumers after receiving a new one.
*
* <p>If the {@code statusConsumer} returns {@link STATUS_SUCCESS}, the caller should call
- * {@link #stopListeningForHotword(Executor, Consumer)} when it wants the wearable to stop
+ * {@link #stopHotwordRecognition(Executor, Consumer)} when it wants the wearable to stop
* listening for hotword. If the {@code statusConsumer} returns any other status code, a failure
- * has occurred and calling {@link #stopListeningForHotword(Executor, Consumer)} is not
+ * has occurred and calling {@link #stopHotwordRecognition(Executor, Consumer)} is not
* required. The system will not retry listening automatically. The caller should call this
* method again if they want to retry.
*
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 821034a..c673d58 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2797,6 +2797,8 @@
public int developmentInstallFlags = 0;
/** {@hide} */
public int unarchiveId = -1;
+ /** {@hide} */
+ public @Nullable String dexoptCompilerFilter = null;
private final ArrayMap<String, Integer> mPermissionStates;
@@ -2850,6 +2852,7 @@
applicationEnabledSettingPersistent = source.readBoolean();
developmentInstallFlags = source.readInt();
unarchiveId = source.readInt();
+ dexoptCompilerFilter = source.readString();
}
/** {@hide} */
@@ -2885,6 +2888,7 @@
ret.applicationEnabledSettingPersistent = applicationEnabledSettingPersistent;
ret.developmentInstallFlags = developmentInstallFlags;
ret.unarchiveId = unarchiveId;
+ ret.dexoptCompilerFilter = dexoptCompilerFilter;
return ret;
}
@@ -3564,6 +3568,11 @@
}
/** @hide */
+ public void setDexoptCompilerFilter(@Nullable String dexoptCompilerFilter) {
+ this.dexoptCompilerFilter = dexoptCompilerFilter;
+ }
+
+ /** @hide */
@NonNull
public ArrayMap<String, Integer> getPermissionStates() {
return mPermissionStates;
@@ -3622,6 +3631,7 @@
applicationEnabledSettingPersistent);
pw.printHexPair("developmentInstallFlags", developmentInstallFlags);
pw.printPair("unarchiveId", unarchiveId);
+ pw.printPair("dexoptCompilerFilter", dexoptCompilerFilter);
pw.println();
}
@@ -3667,6 +3677,7 @@
dest.writeBoolean(applicationEnabledSettingPersistent);
dest.writeInt(developmentInstallFlags);
dest.writeInt(unarchiveId);
+ dest.writeString(dexoptCompilerFilter);
}
public static final Parcelable.Creator<SessionParams>
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index de26384..4819f67 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -2360,11 +2360,8 @@
* <p>If the session configuration is not supported, the AE mode reported in the
* CaptureResult will be 'ON' instead of 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY'.</p>
* <p>When this AE mode is enabled, the CaptureResult field
- * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} will be present and not null. Otherwise, the
- * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} field will not be present in the CaptureResult.</p>
- * <p>The application can observe the CaptureResult field
- * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} to determine when low light boost is 'ACTIVE' or
- * 'INACTIVE'.</p>
+ * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} will indicate when low light boost is 'ACTIVE'
+ * or 'INACTIVE'. By default {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} will be 'INACTIVE'.</p>
* <p>The low light boost is 'ACTIVE' once the scene lighting condition is less than the
* upper bound lux value defined by {@link CameraCharacteristics#CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE android.control.lowLightBoostInfoLuminanceRange}.
* This mode will be 'INACTIVE' once the scene lighting condition is greater than the
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index ef83f9a..d652b4c 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2819,12 +2819,11 @@
* <p>When low light boost is enabled by setting the AE mode to
* 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY', it can dynamically apply a low light
* boost when the light level threshold is exceeded.</p>
- * <p>This field is present in the CaptureResult when the AE mode is set to
- * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY'. Otherwise, the field is not present.</p>
* <p>This state indicates when low light boost is 'ACTIVE' and applied. Similarly, it can
* indicate when it is not being applied by returning 'INACTIVE'.</p>
* <p>This key will be absent from the CaptureResult if AE mode is not set to
* 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY.</p>
+ * <p>The default value will always be 'INACTIVE'.</p>
* <p><b>Possible values:</b></p>
* <ul>
* <li>{@link #CONTROL_LOW_LIGHT_BOOST_STATE_INACTIVE INACTIVE}</li>
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index dda52dd..ebcc371 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -638,13 +638,15 @@
/**
* Create a list of {@link OutputConfiguration} instances for a
- * {@link android.hardware.camera2.params.MultiResolutionImageReader}.
+ * {@link MultiResolutionImageReader}.
*
* <p>This method can be used to create query OutputConfigurations for a
* MultiResolutionImageReader that can be included in a SessionConfiguration passed into
- * {@link CameraDeviceSetup#isSessionConfigurationSupported} before opening and setting up
- * a camera device in full, at which point {@link #setSurfacesForMultiResolutionOutput}
- * can be used to link to the actual MultiResolutionImageReader.</p>
+ * {@link
+ * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported}
+ * before opening and setting up a camera device in full, at which point {@link
+ * #setSurfacesForMultiResolutionOutput} can be used to link to the actual
+ * MultiResolutionImageReader.</p>
*
* <p>This constructor takes same arguments used to create a {@link
* MultiResolutionImageReader}: a collection of {@link MultiResolutionStreamInfo}
@@ -655,12 +657,12 @@
* @param format The format of the MultiResolutionImageReader. This must be one of the {@link
* android.graphics.ImageFormat} or {@link android.graphics.PixelFormat} constants
* supported by the camera device. Note that not all formats are supported, like
- * {@link ImageFormat.NV21}. The supported multi-resolution reader format can be
+ * {@link ImageFormat#NV21}. The supported multi-resolution reader format can be
* queried by {@link MultiResolutionStreamConfigurationMap#getOutputFormats}.
*
* @return The list of {@link OutputConfiguration} objects for a MultiResolutionImageReader.
*
- * @throws IllegaArgumentException If the {@code streams} is null or doesn't contain
+ * @throws IllegalArgumentException If the {@code streams} is null or doesn't contain
* at least 2 items, or if {@code format} isn't a valid camera
* format.
*
@@ -710,7 +712,7 @@
* instances.</p>
*
* @param outputConfigurations The OutputConfiguration objects created by {@link
- * #createInstancesFromMultiResolutionOutput}
+ * #createInstancesForMultiResolutionOutput}
* @param multiResolutionImageReader The MultiResolutionImageReader object created from the same
* MultiResolutionStreamInfo parameters as
* {@code outputConfigurations}.
@@ -759,31 +761,33 @@
* the deferred Surface can be obtained: (1) from {@link android.view.SurfaceView}
* by calling {@link android.view.SurfaceHolder#getSurface}, (2) from
* {@link android.graphics.SurfaceTexture} via
- * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}, (3) from {@link
- * android.media.MediaRecorder} via {@link android.media.MediaRecorder.getSurface} or {@link
- * android.media.MediaCodec#createPersistentInputSurface}, or (4) from {@link
- * android.media.MediaCodce} via {@link android.media.MediaCodec#createInputSurface} or {@link
- * android.media.MediaCodec#createPersistentInputSource}.</p>
+ * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}, (3) from
+ * {@link android.media.MediaRecorder} via {@link android.media.MediaRecorder#getSurface} or
+ * {@link android.media.MediaCodec#createPersistentInputSurface}, or (4) from
+ * {@link android.media.MediaCodec} via {@link android.media.MediaCodec#createInputSurface} or
+ * {@link android.media.MediaCodec#createPersistentInputSurface}.</p>
*
* <ul>
* <li>Surfaces for {@link android.view.SurfaceView} and {@link android.graphics.SurfaceTexture}
* can be deferred until after {@link CameraDevice#createCaptureSession}. In that case, the
* output Surface must be set via {@link #addSurface}, and the Surface configuration must be
- * finalized via {@link CameraCaptureSession#finalizeOutputConfiguration} before submitting
+ * finalized via {@link CameraCaptureSession#finalizeOutputConfigurations} before submitting
* a request with the Surface target.</li>
* <li>For all other target types, the output Surface must be set by {@link #addSurface},
- * and {@link CameraCaptureSession#finalizeOutputConfiguration} is not needed because the
+ * and {@link CameraCaptureSession#finalizeOutputConfigurations} is not needed because the
* OutputConfiguration used to create the session will contain the actual Surface.</li>
* </ul>
*
* <p>Before {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM Android V}, only {@link
* android.view.SurfaceView} and {@link android.graphics.SurfaceTexture} are supported. Both
* kind of outputs can be deferred until after {@link
- * CameraDevice#createCaptureSessionByOutputConfiguration}.</p>
+ * CameraDevice#createCaptureSessionByOutputConfigurations}.</p>
*
* <p>An OutputConfiguration object created by this constructor can be used for {@link
- * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
- * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
+ * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported}
+ * and {@link
+ * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without
+ * having called {@link #addSurface}.</p>
*
* @param surfaceSize Size for the deferred surface.
* @param klass a non-{@code null} {@link Class} object reference that indicates the source of
@@ -849,8 +853,10 @@
* before creating the capture session.</p>
*
* <p>An OutputConfiguration object created by this constructor can be used for {@link
- * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
- * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
+ * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported}
+ * and {@link
+ * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without
+ * having called {@link #addSurface}.</p>
*
* @param format The format of the ImageReader output. This must be one of the
* {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat}
@@ -873,8 +879,10 @@
* before creating the capture session.</p>
*
* <p>An OutputConfiguration object created by this constructor can be used for {@link
- * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
- * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
+ * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported}
+ * and {@link
+ * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without
+ * having called {@link #addSurface}.</p>
*
* @param surfaceGroupId A group ID for this output, used for sharing memory between multiple
* outputs.
@@ -899,8 +907,10 @@
* before creating the capture session.</p>
*
* <p>An OutputConfiguration object created by this constructor can be used for {@link
- * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
- * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
+ * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported}
+ * and {@link
+ * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without
+ * having called {@link #addSurface}.</p>
*
* @param format The format of the ImageReader output. This must be one of the
* {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat}
@@ -923,8 +933,10 @@
* before creating the capture session.</p>
*
* <p>An OutputConfiguration object created by this constructor can be used for {@link
- * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
- * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
+ * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported}
+ * and {@link
+ * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without
+ * having called {@link #addSurface}.</p>
*
* @param surfaceGroupId A group ID for this output, used for sharing memory between multiple
* outputs.
@@ -1171,9 +1183,9 @@
* <li>from {@link android.media.MediaRecorder} by calling
* {@link android.media.MediaRecorder#getSurface} or {@link
* android.media.MediaCodec#createPersistentInputSurface}</li>
- * <li>from {@link android.media.MediaCodce} by calling
- * {@link android.media.MediaCodec#createInputSurface} or {@link
- * android.media.MediaCodec#createPersistentInputSource}</li>
+ * <li>from {@link android.media.MediaCodec} by calling
+ * {@link android.media.MediaCodec#createInputSurface} or
+ * {@link android.media.MediaCodec#createPersistentInputSurface()}</li>
* </ul>
*
* <p> If the OutputConfiguration was constructed by {@link #OutputConfiguration(int, Size)}
diff --git a/core/java/android/hardware/location/ISignificantPlaceProvider.aidl b/core/java/android/hardware/location/ISignificantPlaceProvider.aidl
index e02169e..992dbff 100644
--- a/core/java/android/hardware/location/ISignificantPlaceProvider.aidl
+++ b/core/java/android/hardware/location/ISignificantPlaceProvider.aidl
@@ -7,4 +7,5 @@
*/
oneway interface ISignificantPlaceProvider {
void setSignificantPlaceProviderManager(in ISignificantPlaceProviderManager manager);
+ void onSignificantPlaceCheck();
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 4d69437..943b04f 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -401,10 +401,7 @@
private long mStylusHwSessionsTimeout = STYLUS_HANDWRITING_IDLE_TIMEOUT_MS;
private Runnable mStylusWindowIdleTimeoutRunnable;
private long mStylusWindowIdleTimeoutForTest;
- /**
- * Tracks last {@link MotionEvent#getToolType(int)} used for {@link MotionEvent#ACTION_DOWN}.
- **/
- private int mLastUsedToolType;
+
/**
* Tracks the ctrl+shift shortcut
**/
@@ -1368,7 +1365,6 @@
private void updateEditorToolTypeInternal(int toolType) {
if (Flags.useHandwritingListenerForTooltype()) {
- mLastUsedToolType = toolType;
if (mInputEditorInfo != null) {
mInputEditorInfo.setInitialToolType(toolType);
}
@@ -3385,9 +3381,6 @@
null /* icProto */);
mInputStarted = true;
mStartedInputConnection = ic;
- if (Flags.useHandwritingListenerForTooltype()) {
- editorInfo.setInitialToolType(mLastUsedToolType);
- }
mInputEditorInfo = editorInfo;
initialize();
mInlineSuggestionSessionController.notifyOnStartInput(
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index fdce476..20522fa 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1907,6 +1907,31 @@
"no_near_field_communication_radio";
/**
+ * This user restriction specifies if Near-field communication is disallowed to change
+ * on the device. If Near-field communication is disallowed it cannot be changed via Settings.
+ *
+ * <p>This restriction can only be set by a device owner or a profile owner of an
+ * organization-owned managed profile on the parent profile.
+ * In both cases, the restriction applies globally on the device and will not allow Near-field
+ * communication state being changed.
+ *
+ * <p>
+ * Near-field communication (NFC) is a radio technology that allows two devices (like your phone
+ * and a payments terminal) to communicate with each other when they're close together.
+ *
+ * <p>Default is <code>false</code>.
+ *
+ * <p>Key for user restrictions.
+ * <p>Type: Boolean
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_USER_RESTRICTION)
+ public static final String DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO =
+ "no_change_near_field_communication_radio";
+
+ /**
* This user restriction specifies if Thread network is disallowed on the device. If Thread
* network is disallowed it cannot be turned on via Settings.
*
@@ -2007,6 +2032,7 @@
DISALLOW_CAMERA,
DISALLOW_CAMERA_TOGGLE,
DISALLOW_CELLULAR_2G,
+ DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO,
DISALLOW_CHANGE_WIFI_STATE,
DISALLOW_CONFIG_BLUETOOTH,
DISALLOW_CONFIG_BRIGHTNESS,
diff --git a/core/java/android/service/wearable/WearableSensingService.java b/core/java/android/service/wearable/WearableSensingService.java
index ac22e70..3735c43 100644
--- a/core/java/android/service/wearable/WearableSensingService.java
+++ b/core/java/android/service/wearable/WearableSensingService.java
@@ -398,8 +398,8 @@
/**
* Called when a data request observer is registered. Each request must not be larger than
* {@link WearableSensingDataRequest#getMaxRequestSize()}. In addition, at most {@link
- * WearableSensingDataRequester#getRateLimit()} requests can be sent every rolling {@link
- * WearableSensingDataRequester#getRateLimitWindowSize()}. Requests that are too large or too
+ * WearableSensingDataRequest#getRateLimit()} requests can be sent every rolling {@link
+ * WearableSensingDataRequest#getRateLimitWindowSize()}. Requests that are too large or too
* frequent will be dropped by the system. See {@link
* WearableSensingDataRequester#requestData(WearableSensingDataRequest, Consumer)} for details
* about the status code returned for each request.
@@ -442,7 +442,7 @@
* @param packageName The package name of the app that will receive the requests sent to the
* dataRequester.
* @param dataRequester A handle to the observer to be unregistered. It is the exact same
- * instance provided in a previous {@link #onDataRequestConsumerRegistered(int, String,
+ * instance provided in a previous {@link #onDataRequestObserverRegistered(int, String,
* WearableSensingDataRequester, Consumer)} invocation.
* @param statusConsumer the consumer for the status of the data request observer
* unregistration. This is different from the status for each data request.
@@ -469,7 +469,7 @@
* in which case it should return the corresponding status code.
*
* <p>The implementation should also store the {@code statusConsumer}. If the wearable stops
- * listening for hotword for any reason other than {@link #onStopListeningForHotword(Consumer)}
+ * listening for hotword for any reason other than {@link #onStopHotwordRecognition(Consumer)}
* being invoked, it should send an appropriate status code listed in {@link
* WearableSensingManager} to {@code statusConsumer}. If the error condition cannot be described
* by any of those status codes, it should send a {@link WearableSensingManager#STATUS_UNKNOWN}.
@@ -514,11 +514,11 @@
/**
* Called when hotword audio data sent to the {@code hotwordAudioConsumer} in {@link
- * #onStartListeningForHotword(Consumer, Consumer)} is accepted by the
+ * #onStartHotwordRecognition(Consumer, Consumer)} is accepted by the
* {@link android.service.voice.HotwordDetectionService} as valid hotword.
*
* <p>After the implementation of this class sends the hotword audio data to the {@code
- * hotwordAudioConsumer} in {@link #onStartListeningForHotword(Consumer,
+ * hotwordAudioConsumer} in {@link #onStartHotwordRecognition(Consumer,
* Consumer)}, the system will forward the data into {@link
* android.service.voice.HotwordDetectionService} (which runs in an isolated process) for
* second-stage hotword detection. If accepted as valid hotword there, this method will be
@@ -545,7 +545,7 @@
*
* <p>This method is expected to be overridden by a derived class. The implementation should
* stop sending hotword audio data to the {@code hotwordAudioConsumer} in {@link
- * #onStartListeningForHotword(Consumer, Consumer)}
+ * #onStartHotwordRecognition(Consumer, Consumer)}
*/
@FlaggedApi(Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API)
@BinderThread
diff --git a/core/java/android/view/HdrRenderState.java b/core/java/android/view/HdrRenderState.java
index eadc507..c6b3937 100644
--- a/core/java/android/view/HdrRenderState.java
+++ b/core/java/android/view/HdrRenderState.java
@@ -65,6 +65,7 @@
void startListening() {
if (isHdrEnabled() && !mIsListenerRegistered && mViewRoot.mDisplay != null) {
mViewRoot.mDisplay.registerHdrSdrRatioChangedListener(mViewRoot.mExecutor, this);
+ mIsListenerRegistered = true;
}
}
diff --git a/core/java/android/view/SurfaceControlRegistry.java b/core/java/android/view/SurfaceControlRegistry.java
index 127d4a7..aa3654d 100644
--- a/core/java/android/view/SurfaceControlRegistry.java
+++ b/core/java/android/view/SurfaceControlRegistry.java
@@ -342,12 +342,14 @@
return false;
}
final boolean matchName = !sCallStackDebuggingMatchName.isEmpty();
- if (matchName && (name == null
- || !sCallStackDebuggingMatchName.contains(name.toLowerCase()))) {
- // Skip if target surface doesn't match requested surface
+ if (!matchName) {
+ return true;
+ }
+ if (name == null) {
return false;
}
- return true;
+ return sCallStackDebuggingMatchName.contains(name.toLowerCase()) ||
+ name.toLowerCase().contains(sCallStackDebuggingMatchName);
}
/**
diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java
index f24bc74..57bded7 100644
--- a/core/java/android/window/BackNavigationInfo.java
+++ b/core/java/android/window/BackNavigationInfo.java
@@ -23,6 +23,7 @@
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.graphics.Color;
+import android.graphics.Rect;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -113,6 +114,8 @@
private final CustomAnimationInfo mCustomAnimationInfo;
private final int mLetterboxColor;
+ @NonNull
+ private final Rect mTouchableRegion;
/**
* Create a new {@link BackNavigationInfo} instance.
@@ -128,7 +131,8 @@
boolean isPrepareRemoteAnimation,
boolean isAnimationCallback,
@Nullable CustomAnimationInfo customAnimationInfo,
- int letterboxColor) {
+ int letterboxColor,
+ @Nullable Rect touchableRegion) {
mType = type;
mOnBackNavigationDone = onBackNavigationDone;
mOnBackInvokedCallback = onBackInvokedCallback;
@@ -136,6 +140,7 @@
mAnimationCallback = isAnimationCallback;
mCustomAnimationInfo = customAnimationInfo;
mLetterboxColor = letterboxColor;
+ mTouchableRegion = new Rect(touchableRegion);
}
private BackNavigationInfo(@NonNull Parcel in) {
@@ -146,6 +151,7 @@
mAnimationCallback = in.readBoolean();
mCustomAnimationInfo = in.readTypedObject(CustomAnimationInfo.CREATOR);
mLetterboxColor = in.readInt();
+ mTouchableRegion = in.readTypedObject(Rect.CREATOR);
}
/** @hide */
@@ -158,6 +164,7 @@
dest.writeBoolean(mAnimationCallback);
dest.writeTypedObject(mCustomAnimationInfo, flags);
dest.writeInt(mLetterboxColor);
+ dest.writeTypedObject(mTouchableRegion, flags);
}
/**
@@ -206,6 +213,16 @@
public int getLetterboxColor() {
return mLetterboxColor;
}
+
+ /**
+ * @return The app window region where the client can handle touch event.
+ * @hide
+ */
+ @NonNull
+ public Rect getTouchableRegion() {
+ return mTouchableRegion;
+ }
+
/**
* Callback to be called when the back preview is finished in order to notify the server that
* it can clean up the resources created for the animation.
@@ -402,6 +419,7 @@
private boolean mAnimationCallback = false;
private int mLetterboxColor = Color.TRANSPARENT;
+ private Rect mTouchableRegion;
/**
* @see BackNavigationInfo#getType()
@@ -478,6 +496,13 @@
}
/**
+ * @param rect Non-empty for frame of current focus window.
+ */
+ public Builder setTouchableRegion(Rect rect) {
+ mTouchableRegion = new Rect(rect);
+ return this;
+ }
+ /**
* Builds and returns an instance of {@link BackNavigationInfo}
*/
public BackNavigationInfo build() {
@@ -486,7 +511,8 @@
mPrepareRemoteAnimation,
mAnimationCallback,
mCustomAnimationInfo,
- mLetterboxColor);
+ mLetterboxColor,
+ mTouchableRegion);
}
}
}
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index b7f6f36..4ca64e7 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -435,7 +435,16 @@
}
@Override
- public void onBackProgressed(BackMotionEvent backEvent) { }
+ public void onBackProgressed(BackMotionEvent backEvent) {
+ // This is only called in some special cases such as when activity embedding is active
+ // or when the activity is letterboxed. Otherwise mProgressAnimator#onBackProgressed is
+ // called from WindowOnBackInvokedDispatcher#onMotionEvent
+ mHandler.post(() -> {
+ if (getBackAnimationCallback() != null) {
+ mProgressAnimator.onBackProgressed(backEvent);
+ }
+ });
+ }
@Override
public void onBackCancelled() {
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index b91f2d6..ca125da 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -113,3 +113,10 @@
description: "Introduces a new observer in shell to track the task stack."
bug: "341932484"
}
+
+flag {
+ name: "enable_desktop_windowing_size_constraints"
+ namespace: "lse_desktop_experience"
+ description: "Whether to enable min/max window size constraints when resizing a window in desktop windowing mode"
+ bug: "327589741"
+}
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index cb5af91..d32486c 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -203,55 +203,52 @@
return true;
}
-static void pointerCoordsToNative(JNIEnv* env, jobject pointerCoordsObj,
- float xOffset, float yOffset, PointerCoords* outRawPointerCoords) {
- outRawPointerCoords->clear();
- outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_X,
- env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.x) - xOffset);
- outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_Y,
- env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.y) - yOffset);
- outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
- env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.pressure));
- outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_SIZE,
- env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.size));
- outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR,
- env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMajor));
- outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR,
- env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMinor));
- outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR,
- env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMajor));
- outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR,
- env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMinor));
- outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
- env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.orientation));
- outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X,
- env->GetFloatField(pointerCoordsObj,
- gPointerCoordsClassInfo.relativeX));
- outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y,
- env->GetFloatField(pointerCoordsObj,
- gPointerCoordsClassInfo.relativeY));
- outRawPointerCoords->isResampled =
- env->GetBooleanField(pointerCoordsObj, gPointerCoordsClassInfo.isResampled);
+static PointerCoords pointerCoordsToNative(JNIEnv* env, jobject pointerCoordsObj) {
+ PointerCoords out{};
+ out.setAxisValue(AMOTION_EVENT_AXIS_X,
+ env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.x));
+ out.setAxisValue(AMOTION_EVENT_AXIS_Y,
+ env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.y));
+ out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
+ env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.pressure));
+ out.setAxisValue(AMOTION_EVENT_AXIS_SIZE,
+ env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.size));
+ out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR,
+ env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMajor));
+ out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR,
+ env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMinor));
+ out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR,
+ env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMajor));
+ out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR,
+ env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMinor));
+ out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
+ env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.orientation));
+ out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X,
+ env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.relativeX));
+ out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y,
+ env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.relativeY));
+ out.isResampled = env->GetBooleanField(pointerCoordsObj, gPointerCoordsClassInfo.isResampled);
BitSet64 bits =
BitSet64(env->GetLongField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits));
if (!bits.isEmpty()) {
- jfloatArray valuesArray = jfloatArray(env->GetObjectField(pointerCoordsObj,
- gPointerCoordsClassInfo.mPackedAxisValues));
+ jfloatArray valuesArray = jfloatArray(
+ env->GetObjectField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisValues));
if (valuesArray) {
- jfloat* values = static_cast<jfloat*>(
- env->GetPrimitiveArrayCritical(valuesArray, NULL));
+ jfloat* values =
+ static_cast<jfloat*>(env->GetPrimitiveArrayCritical(valuesArray, NULL));
uint32_t index = 0;
do {
uint32_t axis = bits.clearFirstMarkedBit();
- outRawPointerCoords->setAxisValue(axis, values[index++]);
+ out.setAxisValue(axis, values[index++]);
} while (!bits.isEmpty());
env->ReleasePrimitiveArrayCritical(valuesArray, values, JNI_ABORT);
env->DeleteLocalRef(valuesArray);
}
}
+ return out;
}
static jfloatArray obtainPackedAxisValuesArray(JNIEnv* env, uint32_t minSize,
@@ -303,14 +300,13 @@
env->SetLongField(outPointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits, outBits);
}
-static void pointerPropertiesToNative(JNIEnv* env, jobject pointerPropertiesObj,
- PointerProperties* outPointerProperties) {
- outPointerProperties->clear();
- outPointerProperties->id = env->GetIntField(pointerPropertiesObj,
- gPointerPropertiesClassInfo.id);
- const int32_t toolType = env->GetIntField(pointerPropertiesObj,
- gPointerPropertiesClassInfo.toolType);
- outPointerProperties->toolType = static_cast<ToolType>(toolType);
+static PointerProperties pointerPropertiesToNative(JNIEnv* env, jobject pointerPropertiesObj) {
+ PointerProperties out{};
+ out.id = env->GetIntField(pointerPropertiesObj, gPointerPropertiesClassInfo.id);
+ const int32_t toolType =
+ env->GetIntField(pointerPropertiesObj, gPointerPropertiesClassInfo.toolType);
+ out.toolType = static_cast<ToolType>(toolType);
+ return out;
}
static void pointerPropertiesFromNative(JNIEnv* env, const PointerProperties* pointerProperties,
@@ -343,15 +339,21 @@
event = std::make_unique<MotionEvent>();
}
- PointerProperties pointerProperties[pointerCount];
- PointerCoords rawPointerCoords[pointerCount];
+ ui::Transform transform;
+ transform.set(xOffset, yOffset);
+ const ui::Transform inverseTransform = transform.inverse();
+
+ std::vector<PointerProperties> pointerProperties;
+ pointerProperties.reserve(pointerCount);
+ std::vector<PointerCoords> rawPointerCoords;
+ rawPointerCoords.reserve(pointerCount);
for (jint i = 0; i < pointerCount; i++) {
jobject pointerPropertiesObj = env->GetObjectArrayElement(pointerPropertiesObjArray, i);
if (!pointerPropertiesObj) {
return 0;
}
- pointerPropertiesToNative(env, pointerPropertiesObj, &pointerProperties[i]);
+ pointerProperties.emplace_back(pointerPropertiesToNative(env, pointerPropertiesObj));
env->DeleteLocalRef(pointerPropertiesObj);
jobject pointerCoordsObj = env->GetObjectArrayElement(pointerCoordsObjArray, i);
@@ -359,23 +361,24 @@
jniThrowNullPointerException(env, "pointerCoords");
return 0;
}
- pointerCoordsToNative(env, pointerCoordsObj, xOffset, yOffset, &rawPointerCoords[i]);
- if (rawPointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION) != 0.f) {
+ rawPointerCoords.emplace_back(pointerCoordsToNative(env, pointerCoordsObj));
+ PointerCoords& coords = rawPointerCoords.back();
+ if (coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION) != 0.f) {
flags |= AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION;
}
+ MotionEvent::calculateTransformedCoordsInPlace(coords, source, flags, inverseTransform);
env->DeleteLocalRef(pointerCoordsObj);
}
- ui::Transform transform;
- transform.set(xOffset, yOffset);
- ui::Transform identityTransform;
+ static const ui::Transform kIdentityTransform;
event->initialize(InputEvent::nextId(), deviceId, source, ui::LogicalDisplayId{displayId},
INVALID_HMAC, action, 0, flags, edgeFlags, metaState, buttonState,
static_cast<MotionClassification>(classification), transform, xPrecision,
yPrecision, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, downTimeNanos,
- eventTimeNanos, pointerCount, pointerProperties, rawPointerCoords);
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, kIdentityTransform, downTimeNanos,
+ eventTimeNanos, pointerCount, pointerProperties.data(),
+ rawPointerCoords.data());
return reinterpret_cast<jlong>(event.release());
}
@@ -395,7 +398,10 @@
return;
}
- PointerCoords rawPointerCoords[pointerCount];
+ const ui::Transform inverseTransform = event->getTransform().inverse();
+
+ std::vector<PointerCoords> rawPointerCoords;
+ rawPointerCoords.reserve(pointerCount);
for (size_t i = 0; i < pointerCount; i++) {
jobject pointerCoordsObj = env->GetObjectArrayElement(pointerCoordsObjArray, i);
@@ -403,12 +409,13 @@
jniThrowNullPointerException(env, "pointerCoords");
return;
}
- pointerCoordsToNative(env, pointerCoordsObj, event->getRawXOffset(), event->getRawYOffset(),
- &rawPointerCoords[i]);
+ rawPointerCoords.emplace_back(pointerCoordsToNative(env, pointerCoordsObj));
+ MotionEvent::calculateTransformedCoordsInPlace(rawPointerCoords.back(), event->getSource(),
+ event->getFlags(), inverseTransform);
env->DeleteLocalRef(pointerCoordsObj);
}
- event->addSample(eventTimeNanos, rawPointerCoords);
+ event->addSample(eventTimeNanos, rawPointerCoords.data());
event->setMetaState(event->getMetaState() | metaState);
}
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java
index 4f9b269..4c3d4e3 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java
@@ -16,8 +16,6 @@
package android.hardware.radio;
-import static com.google.common.truth.Truth.assertWithMessage;
-
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -36,6 +34,8 @@
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArrayMap;
+import com.google.common.truth.Expect;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -155,6 +155,9 @@
private RadioManager mRadioManager;
private final ApplicationInfo mApplicationInfo = new ApplicationInfo();
+ @Rule
+ public final Expect mExpect = Expect.create();
+
@Mock
private IRadioService mRadioServiceMock;
@Mock
@@ -175,7 +178,7 @@
() -> new RadioManager.AmBandDescriptor(REGION, /* type= */ 100, AM_LOWER_LIMIT,
AM_UPPER_LIMIT, AM_SPACING, STEREO_SUPPORTED));
- assertWithMessage("Unsupported band type exception")
+ mExpect.withMessage("Unsupported band type exception")
.that(thrown).hasMessageThat().contains("Unsupported band");
}
@@ -183,7 +186,7 @@
public void getType_forBandDescriptor() {
RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor();
- assertWithMessage("AM Band Descriptor type")
+ mExpect.withMessage("AM Band Descriptor type")
.that(bandDescriptor.getType()).isEqualTo(RadioManager.BAND_AM);
}
@@ -191,7 +194,7 @@
public void getRegion_forBandDescriptor() {
RadioManager.BandDescriptor bandDescriptor = createFmBandDescriptor();
- assertWithMessage("FM Band Descriptor region")
+ mExpect.withMessage("FM Band Descriptor region")
.that(bandDescriptor.getRegion()).isEqualTo(REGION);
}
@@ -199,7 +202,7 @@
public void getLowerLimit_forBandDescriptor() {
RadioManager.BandDescriptor bandDescriptor = createFmBandDescriptor();
- assertWithMessage("FM Band Descriptor lower limit")
+ mExpect.withMessage("FM Band Descriptor lower limit")
.that(bandDescriptor.getLowerLimit()).isEqualTo(FM_LOWER_LIMIT);
}
@@ -207,7 +210,7 @@
public void getUpperLimit_forBandDescriptor() {
RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor();
- assertWithMessage("AM Band Descriptor upper limit")
+ mExpect.withMessage("AM Band Descriptor upper limit")
.that(bandDescriptor.getUpperLimit()).isEqualTo(AM_UPPER_LIMIT);
}
@@ -215,7 +218,7 @@
public void getSpacing_forBandDescriptor() {
RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor();
- assertWithMessage("AM Band Descriptor spacing")
+ mExpect.withMessage("AM Band Descriptor spacing")
.that(bandDescriptor.getSpacing()).isEqualTo(AM_SPACING);
}
@@ -223,7 +226,7 @@
public void describeContents_forBandDescriptor() {
RadioManager.BandDescriptor bandDescriptor = createFmBandDescriptor();
- assertWithMessage("Band Descriptor contents")
+ mExpect.withMessage("Band Descriptor contents")
.that(bandDescriptor.describeContents()).isEqualTo(0);
}
@@ -237,7 +240,7 @@
RadioManager.BandDescriptor bandDescriptorFromParcel =
RadioManager.BandDescriptor.CREATOR.createFromParcel(parcel);
- assertWithMessage("Band Descriptor created from parcel")
+ mExpect.withMessage("Band Descriptor created from parcel")
.that(bandDescriptorFromParcel).isEqualTo(bandDescriptor);
}
@@ -246,14 +249,14 @@
RadioManager.BandDescriptor[] bandDescriptors =
RadioManager.BandDescriptor.CREATOR.newArray(CREATOR_ARRAY_SIZE);
- assertWithMessage("Band Descriptors").that(bandDescriptors).hasLength(CREATOR_ARRAY_SIZE);
+ mExpect.withMessage("Band Descriptors").that(bandDescriptors).hasLength(CREATOR_ARRAY_SIZE);
}
@Test
public void isAmBand_forAmBandDescriptor_returnsTrue() {
RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor();
- assertWithMessage("Is AM Band Descriptor an AM band")
+ mExpect.withMessage("Is AM Band Descriptor an AM band")
.that(bandDescriptor.isAmBand()).isTrue();
}
@@ -261,43 +264,43 @@
public void isFmBand_forAmBandDescriptor_returnsFalse() {
RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor();
- assertWithMessage("Is AM Band Descriptor an FM band")
+ mExpect.withMessage("Is AM Band Descriptor an FM band")
.that(bandDescriptor.isFmBand()).isFalse();
}
@Test
public void isStereoSupported_forFmBandDescriptor() {
- assertWithMessage("FM Band Descriptor stereo")
+ mExpect.withMessage("FM Band Descriptor stereo")
.that(FM_BAND_DESCRIPTOR.isStereoSupported()).isEqualTo(STEREO_SUPPORTED);
}
@Test
public void isRdsSupported_forFmBandDescriptor() {
- assertWithMessage("FM Band Descriptor RDS or RBDS")
+ mExpect.withMessage("FM Band Descriptor RDS or RBDS")
.that(FM_BAND_DESCRIPTOR.isRdsSupported()).isEqualTo(RDS_SUPPORTED);
}
@Test
public void isTaSupported_forFmBandDescriptor() {
- assertWithMessage("FM Band Descriptor traffic announcement")
+ mExpect.withMessage("FM Band Descriptor traffic announcement")
.that(FM_BAND_DESCRIPTOR.isTaSupported()).isEqualTo(TA_SUPPORTED);
}
@Test
public void isAfSupported_forFmBandDescriptor() {
- assertWithMessage("FM Band Descriptor alternate frequency")
+ mExpect.withMessage("FM Band Descriptor alternate frequency")
.that(FM_BAND_DESCRIPTOR.isAfSupported()).isEqualTo(AF_SUPPORTED);
}
@Test
public void isEaSupported_forFmBandDescriptor() {
- assertWithMessage("FM Band Descriptor emergency announcement")
+ mExpect.withMessage("FM Band Descriptor emergency announcement")
.that(FM_BAND_DESCRIPTOR.isEaSupported()).isEqualTo(EA_SUPPORTED);
}
@Test
public void describeContents_forFmBandDescriptor() {
- assertWithMessage("FM Band Descriptor contents")
+ mExpect.withMessage("FM Band Descriptor contents")
.that(FM_BAND_DESCRIPTOR.describeContents()).isEqualTo(0);
}
@@ -310,7 +313,7 @@
RadioManager.FmBandDescriptor fmBandDescriptorFromParcel =
RadioManager.FmBandDescriptor.CREATOR.createFromParcel(parcel);
- assertWithMessage("FM Band Descriptor created from parcel")
+ mExpect.withMessage("FM Band Descriptor created from parcel")
.that(fmBandDescriptorFromParcel).isEqualTo(FM_BAND_DESCRIPTOR);
}
@@ -319,19 +322,19 @@
RadioManager.FmBandDescriptor[] fmBandDescriptors =
RadioManager.FmBandDescriptor.CREATOR.newArray(CREATOR_ARRAY_SIZE);
- assertWithMessage("FM Band Descriptors")
+ mExpect.withMessage("FM Band Descriptors")
.that(fmBandDescriptors).hasLength(CREATOR_ARRAY_SIZE);
}
@Test
public void isStereoSupported_forAmBandDescriptor() {
- assertWithMessage("AM Band Descriptor stereo")
+ mExpect.withMessage("AM Band Descriptor stereo")
.that(AM_BAND_DESCRIPTOR.isStereoSupported()).isEqualTo(STEREO_SUPPORTED);
}
@Test
public void describeContents_forAmBandDescriptor() {
- assertWithMessage("AM Band Descriptor contents")
+ mExpect.withMessage("AM Band Descriptor contents")
.that(AM_BAND_DESCRIPTOR.describeContents()).isEqualTo(0);
}
@@ -344,7 +347,7 @@
RadioManager.AmBandDescriptor amBandDescriptorFromParcel =
RadioManager.AmBandDescriptor.CREATOR.createFromParcel(parcel);
- assertWithMessage("FM Band Descriptor created from parcel")
+ mExpect.withMessage("FM Band Descriptor created from parcel")
.that(amBandDescriptorFromParcel).isEqualTo(AM_BAND_DESCRIPTOR);
}
@@ -353,7 +356,7 @@
RadioManager.AmBandDescriptor[] amBandDescriptors =
RadioManager.AmBandDescriptor.CREATOR.newArray(CREATOR_ARRAY_SIZE);
- assertWithMessage("AM Band Descriptors")
+ mExpect.withMessage("AM Band Descriptors")
.that(amBandDescriptors).hasLength(CREATOR_ARRAY_SIZE);
}
@@ -361,7 +364,7 @@
public void equals_withSameFmBandDescriptors_returnsTrue() {
RadioManager.FmBandDescriptor fmBandDescriptorCompared = createFmBandDescriptor();
- assertWithMessage("The same FM Band Descriptor")
+ mExpect.withMessage("The same FM Band Descriptor")
.that(FM_BAND_DESCRIPTOR).isEqualTo(fmBandDescriptorCompared);
}
@@ -369,19 +372,19 @@
public void equals_withSameAmBandDescriptors_returnsTrue() {
RadioManager.AmBandDescriptor amBandDescriptorCompared = createAmBandDescriptor();
- assertWithMessage("The same AM Band Descriptor")
+ mExpect.withMessage("The same AM Band Descriptor")
.that(AM_BAND_DESCRIPTOR).isEqualTo(amBandDescriptorCompared);
}
@Test
public void equals_withAmBandDescriptorsAndOtherTypeObject() {
- assertWithMessage("AM Band Descriptor")
+ mExpect.withMessage("AM Band Descriptor")
.that(AM_BAND_DESCRIPTOR).isNotEqualTo(FM_BAND_DESCRIPTOR);
}
@Test
public void equals_withFmBandDescriptorsAndOtherTypeObject() {
- assertWithMessage("FM Band Descriptor")
+ mExpect.withMessage("FM Band Descriptor")
.that(FM_BAND_DESCRIPTOR).isNotEqualTo(AM_BAND_DESCRIPTOR);
}
@@ -391,7 +394,7 @@
new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM, AM_LOWER_LIMIT,
AM_UPPER_LIMIT + AM_SPACING, AM_SPACING, STEREO_SUPPORTED);
- assertWithMessage("AM Band Descriptor of different upper limit")
+ mExpect.withMessage("AM Band Descriptor of different upper limit")
.that(AM_BAND_DESCRIPTOR).isNotEqualTo(amBandDescriptorCompared);
}
@@ -401,7 +404,7 @@
new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM, AM_LOWER_LIMIT,
AM_UPPER_LIMIT, AM_SPACING, !STEREO_SUPPORTED);
- assertWithMessage("AM Band Descriptor of different stereo support values")
+ mExpect.withMessage("AM Band Descriptor of different stereo support values")
.that(AM_BAND_DESCRIPTOR).isNotEqualTo(amBandDescriptorCompared);
}
@@ -411,7 +414,7 @@
REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING * 2,
STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED);
- assertWithMessage("FM Band Descriptors of different support limit values")
+ mExpect.withMessage("FM Band Descriptors of different support limit values")
.that(FM_BAND_DESCRIPTOR).isNotEqualTo(fmBandDescriptorCompared);
}
@@ -421,7 +424,7 @@
REGION + 1, RadioManager.BAND_AM_HD, AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING,
STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED);
- assertWithMessage("FM Band Descriptors of different region values")
+ mExpect.withMessage("FM Band Descriptors of different region values")
.that(FM_BAND_DESCRIPTOR).isNotEqualTo(fmBandDescriptorCompared);
}
@@ -431,7 +434,7 @@
REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING,
!STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED);
- assertWithMessage("FM Band Descriptors of different stereo support values")
+ mExpect.withMessage("FM Band Descriptors of different stereo support values")
.that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR);
}
@@ -441,7 +444,7 @@
REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING,
STEREO_SUPPORTED, !RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED);
- assertWithMessage("FM Band Descriptors of different rds support values")
+ mExpect.withMessage("FM Band Descriptors of different rds support values")
.that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR);
}
@@ -451,7 +454,7 @@
REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING,
STEREO_SUPPORTED, RDS_SUPPORTED, !TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED);
- assertWithMessage("FM Band Descriptors of different ta support values")
+ mExpect.withMessage("FM Band Descriptors of different ta support values")
.that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR);
}
@@ -461,7 +464,7 @@
REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING,
STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, !AF_SUPPORTED, EA_SUPPORTED);
- assertWithMessage("FM Band Descriptors of different af support values")
+ mExpect.withMessage("FM Band Descriptors of different af support values")
.that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR);
}
@@ -471,7 +474,7 @@
REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING,
STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, !EA_SUPPORTED);
- assertWithMessage("FM Band Descriptors of different ea support values")
+ mExpect.withMessage("FM Band Descriptors of different ea support values")
.that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR);
}
@@ -479,7 +482,7 @@
public void hashCode_withSameFmBandDescriptors_equals() {
RadioManager.FmBandDescriptor fmBandDescriptorCompared = createFmBandDescriptor();
- assertWithMessage("Hash code of the same FM Band Descriptor")
+ mExpect.withMessage("Hash code of the same FM Band Descriptor")
.that(fmBandDescriptorCompared.hashCode()).isEqualTo(FM_BAND_DESCRIPTOR.hashCode());
}
@@ -487,7 +490,7 @@
public void hashCode_withSameAmBandDescriptors_equals() {
RadioManager.AmBandDescriptor amBandDescriptorCompared = createAmBandDescriptor();
- assertWithMessage("Hash code of the same AM Band Descriptor")
+ mExpect.withMessage("Hash code of the same AM Band Descriptor")
.that(amBandDescriptorCompared.hashCode()).isEqualTo(AM_BAND_DESCRIPTOR.hashCode());
}
@@ -497,7 +500,7 @@
REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING,
STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, !AF_SUPPORTED, EA_SUPPORTED);
- assertWithMessage("Hash code of FM Band Descriptor of different spacing")
+ mExpect.withMessage("Hash code of FM Band Descriptor of different spacing")
.that(fmBandDescriptorCompared.hashCode())
.isNotEqualTo(FM_BAND_DESCRIPTOR.hashCode());
}
@@ -508,7 +511,7 @@
new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM, AM_LOWER_LIMIT,
AM_UPPER_LIMIT, AM_SPACING * 2, STEREO_SUPPORTED);
- assertWithMessage("Hash code of AM Band Descriptor of different spacing")
+ mExpect.withMessage("Hash code of AM Band Descriptor of different spacing")
.that(amBandDescriptorCompared.hashCode())
.isNotEqualTo(AM_BAND_DESCRIPTOR.hashCode());
}
@@ -517,7 +520,7 @@
public void getType_forBandConfig() {
RadioManager.BandConfig fmBandConfig = createFmBandConfig();
- assertWithMessage("FM Band Config type")
+ mExpect.withMessage("FM Band Config type")
.that(fmBandConfig.getType()).isEqualTo(RadioManager.BAND_FM);
}
@@ -525,7 +528,7 @@
public void getRegion_forBandConfig() {
RadioManager.BandConfig amBandConfig = createAmBandConfig();
- assertWithMessage("AM Band Config region")
+ mExpect.withMessage("AM Band Config region")
.that(amBandConfig.getRegion()).isEqualTo(REGION);
}
@@ -533,7 +536,7 @@
public void getLowerLimit_forBandConfig() {
RadioManager.BandConfig amBandConfig = createAmBandConfig();
- assertWithMessage("AM Band Config lower limit")
+ mExpect.withMessage("AM Band Config lower limit")
.that(amBandConfig.getLowerLimit()).isEqualTo(AM_LOWER_LIMIT);
}
@@ -541,7 +544,7 @@
public void getUpperLimit_forBandConfig() {
RadioManager.BandConfig fmBandConfig = createFmBandConfig();
- assertWithMessage("FM Band Config upper limit")
+ mExpect.withMessage("FM Band Config upper limit")
.that(fmBandConfig.getUpperLimit()).isEqualTo(FM_UPPER_LIMIT);
}
@@ -549,7 +552,7 @@
public void getSpacing_forBandConfig() {
RadioManager.BandConfig fmBandConfig = createFmBandConfig();
- assertWithMessage("FM Band Config spacing")
+ mExpect.withMessage("FM Band Config spacing")
.that(fmBandConfig.getSpacing()).isEqualTo(FM_SPACING);
}
@@ -557,7 +560,7 @@
public void describeContents_forBandConfig() {
RadioManager.BandConfig bandConfig = createFmBandConfig();
- assertWithMessage("FM Band Config contents")
+ mExpect.withMessage("FM Band Config contents")
.that(bandConfig.describeContents()).isEqualTo(0);
}
@@ -571,7 +574,7 @@
RadioManager.BandConfig bandConfigFromParcel =
RadioManager.BandConfig.CREATOR.createFromParcel(parcel);
- assertWithMessage("Band Config created from parcel")
+ mExpect.withMessage("Band Config created from parcel")
.that(bandConfigFromParcel).isEqualTo(bandConfig);
}
@@ -580,42 +583,42 @@
RadioManager.BandConfig[] bandConfigs =
RadioManager.BandConfig.CREATOR.newArray(CREATOR_ARRAY_SIZE);
- assertWithMessage("Band Configs").that(bandConfigs).hasLength(CREATOR_ARRAY_SIZE);
+ mExpect.withMessage("Band Configs").that(bandConfigs).hasLength(CREATOR_ARRAY_SIZE);
}
@Test
public void getStereo_forFmBandConfig() {
- assertWithMessage("FM Band Config stereo")
+ mExpect.withMessage("FM Band Config stereo")
.that(FM_BAND_CONFIG.getStereo()).isEqualTo(STEREO_SUPPORTED);
}
@Test
public void getRds_forFmBandConfig() {
- assertWithMessage("FM Band Config RDS or RBDS")
+ mExpect.withMessage("FM Band Config RDS or RBDS")
.that(FM_BAND_CONFIG.getRds()).isEqualTo(RDS_SUPPORTED);
}
@Test
public void getTa_forFmBandConfig() {
- assertWithMessage("FM Band Config traffic announcement")
+ mExpect.withMessage("FM Band Config traffic announcement")
.that(FM_BAND_CONFIG.getTa()).isEqualTo(TA_SUPPORTED);
}
@Test
public void getAf_forFmBandConfig() {
- assertWithMessage("FM Band Config alternate frequency")
+ mExpect.withMessage("FM Band Config alternate frequency")
.that(FM_BAND_CONFIG.getAf()).isEqualTo(AF_SUPPORTED);
}
@Test
public void getEa_forFmBandConfig() {
- assertWithMessage("FM Band Config emergency Announcement")
+ mExpect.withMessage("FM Band Config emergency Announcement")
.that(FM_BAND_CONFIG.getEa()).isEqualTo(EA_SUPPORTED);
}
@Test
public void describeContents_forFmBandConfig() {
- assertWithMessage("FM Band Config contents")
+ mExpect.withMessage("FM Band Config contents")
.that(FM_BAND_CONFIG.describeContents()).isEqualTo(0);
}
@@ -628,7 +631,7 @@
RadioManager.FmBandConfig fmBandConfigFromParcel =
RadioManager.FmBandConfig.CREATOR.createFromParcel(parcel);
- assertWithMessage("FM Band Config created from parcel")
+ mExpect.withMessage("FM Band Config created from parcel")
.that(fmBandConfigFromParcel).isEqualTo(FM_BAND_CONFIG);
}
@@ -637,18 +640,18 @@
RadioManager.FmBandConfig[] fmBandConfigs =
RadioManager.FmBandConfig.CREATOR.newArray(CREATOR_ARRAY_SIZE);
- assertWithMessage("FM Band Configs").that(fmBandConfigs).hasLength(CREATOR_ARRAY_SIZE);
+ mExpect.withMessage("FM Band Configs").that(fmBandConfigs).hasLength(CREATOR_ARRAY_SIZE);
}
@Test
public void getStereo_forAmBandConfig() {
- assertWithMessage("AM Band Config stereo")
+ mExpect.withMessage("AM Band Config stereo")
.that(AM_BAND_CONFIG.getStereo()).isEqualTo(STEREO_SUPPORTED);
}
@Test
public void describeContents_forAmBandConfig() {
- assertWithMessage("AM Band Config contents")
+ mExpect.withMessage("AM Band Config contents")
.that(AM_BAND_CONFIG.describeContents()).isEqualTo(0);
}
@@ -661,7 +664,7 @@
RadioManager.AmBandConfig amBandConfigFromParcel =
RadioManager.AmBandConfig.CREATOR.createFromParcel(parcel);
- assertWithMessage("AM Band Config created from parcel")
+ mExpect.withMessage("AM Band Config created from parcel")
.that(amBandConfigFromParcel).isEqualTo(AM_BAND_CONFIG);
}
@@ -670,7 +673,7 @@
RadioManager.AmBandConfig[] amBandConfigs =
RadioManager.AmBandConfig.CREATOR.newArray(CREATOR_ARRAY_SIZE);
- assertWithMessage("AM Band Configs").that(amBandConfigs).hasLength(CREATOR_ARRAY_SIZE);
+ mExpect.withMessage("AM Band Configs").that(amBandConfigs).hasLength(CREATOR_ARRAY_SIZE);
}
@Test
@@ -679,7 +682,7 @@
new RadioManager.FmBandConfig.Builder(FM_BAND_CONFIG);
RadioManager.FmBandConfig fmBandConfigCompared = builder.build();
- assertWithMessage("The same FM Band Config")
+ mExpect.withMessage("The same FM Band Config")
.that(FM_BAND_CONFIG).isEqualTo(fmBandConfigCompared);
}
@@ -690,7 +693,7 @@
AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED,
TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED));
- assertWithMessage("FM Band Config of different regions")
+ mExpect.withMessage("FM Band Config of different regions")
.that(FM_BAND_CONFIG).isNotEqualTo(fmBandConfigCompared);
}
@@ -701,7 +704,7 @@
FM_UPPER_LIMIT, FM_SPACING, !STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED,
AF_SUPPORTED, EA_SUPPORTED));
- assertWithMessage("FM Band Config with different stereo support values")
+ mExpect.withMessage("FM Band Config with different stereo support values")
.that(fmBandConfigCompared).isNotEqualTo(FM_BAND_CONFIG);
}
@@ -712,7 +715,7 @@
FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, !RDS_SUPPORTED, TA_SUPPORTED,
AF_SUPPORTED, EA_SUPPORTED));
- assertWithMessage("FM Band Config with different RDS support values")
+ mExpect.withMessage("FM Band Config with different RDS support values")
.that(fmBandConfigCompared).isNotEqualTo(FM_BAND_CONFIG);
}
@@ -723,7 +726,7 @@
FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, !TA_SUPPORTED,
AF_SUPPORTED, EA_SUPPORTED));
- assertWithMessage("FM Band Configs with different ta values")
+ mExpect.withMessage("FM Band Configs with different ta values")
.that(fmBandConfigCompared).isNotEqualTo(FM_BAND_CONFIG);
}
@@ -734,7 +737,7 @@
.setTa(TA_SUPPORTED).setAf(!AF_SUPPORTED).setEa(EA_SUPPORTED);
RadioManager.FmBandConfig fmBandConfigCompared = builder.build();
- assertWithMessage("FM Band Config of different af support value")
+ mExpect.withMessage("FM Band Config of different af support value")
.that(FM_BAND_CONFIG).isNotEqualTo(fmBandConfigCompared);
}
@@ -745,19 +748,19 @@
FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED,
AF_SUPPORTED, !EA_SUPPORTED));
- assertWithMessage("FM Band Configs with different ea support values")
+ mExpect.withMessage("FM Band Configs with different ea support values")
.that(fmBandConfigCompared).isNotEqualTo(FM_BAND_CONFIG);
}
@Test
public void equals_withAmBandConfigsAndOtherTypeObject() {
- assertWithMessage("AM Band Config")
+ mExpect.withMessage("AM Band Config")
.that(AM_BAND_CONFIG).isNotEqualTo(FM_BAND_CONFIG);
}
@Test
public void equals_withFmBandConfigsAndOtherTypeObject() {
- assertWithMessage("FM Band Config")
+ mExpect.withMessage("FM Band Config")
.that(FM_BAND_CONFIG).isNotEqualTo(AM_BAND_CONFIG);
}
@@ -767,7 +770,7 @@
new RadioManager.AmBandConfig.Builder(AM_BAND_CONFIG);
RadioManager.AmBandConfig amBandConfigCompared = builder.build();
- assertWithMessage("The same AM Band Config")
+ mExpect.withMessage("The same AM Band Config")
.that(AM_BAND_CONFIG).isEqualTo(amBandConfigCompared);
}
@@ -777,7 +780,7 @@
new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM_HD, AM_LOWER_LIMIT,
AM_UPPER_LIMIT, AM_SPACING, STEREO_SUPPORTED));
- assertWithMessage("AM Band Config of different type")
+ mExpect.withMessage("AM Band Config of different type")
.that(AM_BAND_CONFIG).isNotEqualTo(amBandConfigCompared);
}
@@ -787,7 +790,7 @@
createAmBandDescriptor()).setStereo(!STEREO_SUPPORTED);
RadioManager.AmBandConfig amBandConfigFromBuilder = builder.build();
- assertWithMessage("AM Band Config of different stereo value")
+ mExpect.withMessage("AM Band Config of different stereo value")
.that(AM_BAND_CONFIG).isNotEqualTo(amBandConfigFromBuilder);
}
@@ -795,7 +798,7 @@
public void hashCode_withSameFmBandConfigs_equals() {
RadioManager.FmBandConfig fmBandConfigCompared = createFmBandConfig();
- assertWithMessage("Hash code of the same FM Band Config")
+ mExpect.withMessage("Hash code of the same FM Band Config")
.that(FM_BAND_CONFIG.hashCode()).isEqualTo(fmBandConfigCompared.hashCode());
}
@@ -803,7 +806,7 @@
public void hashCode_withSameAmBandConfigs_equals() {
RadioManager.AmBandConfig amBandConfigCompared = createAmBandConfig();
- assertWithMessage("Hash code of the same AM Band Config")
+ mExpect.withMessage("Hash code of the same AM Band Config")
.that(amBandConfigCompared.hashCode()).isEqualTo(AM_BAND_CONFIG.hashCode());
}
@@ -814,7 +817,7 @@
FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED,
AF_SUPPORTED, EA_SUPPORTED));
- assertWithMessage("Hash code of FM Band Config with different type")
+ mExpect.withMessage("Hash code of FM Band Config with different type")
.that(fmBandConfigCompared.hashCode()).isNotEqualTo(FM_BAND_CONFIG.hashCode());
}
@@ -824,87 +827,87 @@
new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM, AM_LOWER_LIMIT,
AM_UPPER_LIMIT, AM_SPACING, !STEREO_SUPPORTED));
- assertWithMessage("Hash code of AM Band Config with different stereo support")
+ mExpect.withMessage("Hash code of AM Band Config with different stereo support")
.that(amBandConfigCompared.hashCode()).isNotEqualTo(AM_BAND_CONFIG.hashCode());
}
@Test
public void getId_forModuleProperties() {
- assertWithMessage("Properties id")
+ mExpect.withMessage("Properties id")
.that(AMFM_PROPERTIES.getId()).isEqualTo(PROPERTIES_ID);
}
@Test
public void getServiceName_forModuleProperties() {
- assertWithMessage("Properties service name")
+ mExpect.withMessage("Properties service name")
.that(AMFM_PROPERTIES.getServiceName()).isEqualTo(SERVICE_NAME);
}
@Test
public void getClassId_forModuleProperties() {
- assertWithMessage("Properties class ID")
+ mExpect.withMessage("Properties class ID")
.that(AMFM_PROPERTIES.getClassId()).isEqualTo(CLASS_ID);
}
@Test
public void getImplementor_forModuleProperties() {
- assertWithMessage("Properties implementor")
+ mExpect.withMessage("Properties implementor")
.that(AMFM_PROPERTIES.getImplementor()).isEqualTo(IMPLEMENTOR);
}
@Test
public void getProduct_forModuleProperties() {
- assertWithMessage("Properties product")
+ mExpect.withMessage("Properties product")
.that(AMFM_PROPERTIES.getProduct()).isEqualTo(PRODUCT);
}
@Test
public void getVersion_forModuleProperties() {
- assertWithMessage("Properties version")
+ mExpect.withMessage("Properties version")
.that(AMFM_PROPERTIES.getVersion()).isEqualTo(VERSION);
}
@Test
public void getSerial_forModuleProperties() {
- assertWithMessage("Serial properties")
+ mExpect.withMessage("Serial properties")
.that(AMFM_PROPERTIES.getSerial()).isEqualTo(SERIAL);
}
@Test
public void getNumTuners_forModuleProperties() {
- assertWithMessage("Number of tuners in properties")
+ mExpect.withMessage("Number of tuners in properties")
.that(AMFM_PROPERTIES.getNumTuners()).isEqualTo(NUM_TUNERS);
}
@Test
public void getNumAudioSources_forModuleProperties() {
- assertWithMessage("Number of audio sources in properties")
+ mExpect.withMessage("Number of audio sources in properties")
.that(AMFM_PROPERTIES.getNumAudioSources()).isEqualTo(NUM_AUDIO_SOURCES);
}
@Test
public void isInitializationRequired_forModuleProperties() {
- assertWithMessage("Initialization required in properties")
+ mExpect.withMessage("Initialization required in properties")
.that(AMFM_PROPERTIES.isInitializationRequired())
.isEqualTo(IS_INITIALIZATION_REQUIRED);
}
@Test
public void isCaptureSupported_forModuleProperties() {
- assertWithMessage("Capture support in properties")
+ mExpect.withMessage("Capture support in properties")
.that(AMFM_PROPERTIES.isCaptureSupported()).isEqualTo(IS_CAPTURE_SUPPORTED);
}
@Test
public void isBackgroundScanningSupported_forModuleProperties() {
- assertWithMessage("Background scan support in properties")
+ mExpect.withMessage("Background scan support in properties")
.that(AMFM_PROPERTIES.isBackgroundScanningSupported())
.isEqualTo(IS_BG_SCAN_SUPPORTED);
}
@Test
public void isProgramTypeSupported_withSupportedType_forModuleProperties() {
- assertWithMessage("AM/FM frequency type radio support in properties")
+ mExpect.withMessage("AM/FM frequency type radio support in properties")
.that(AMFM_PROPERTIES.isProgramTypeSupported(
ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY))
.isTrue();
@@ -912,28 +915,28 @@
@Test
public void isProgramTypeSupported_withNonSupportedType_forModuleProperties() {
- assertWithMessage("DAB frequency type radio support in properties")
+ mExpect.withMessage("DAB frequency type radio support in properties")
.that(AMFM_PROPERTIES.isProgramTypeSupported(
ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY)).isFalse();
}
@Test
public void isProgramIdentifierSupported_withSupportedIdentifier_forModuleProperties() {
- assertWithMessage("AM/FM frequency identifier radio support in properties")
+ mExpect.withMessage("AM/FM frequency identifier radio support in properties")
.that(AMFM_PROPERTIES.isProgramIdentifierSupported(
ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY)).isTrue();
}
@Test
public void isProgramIdentifierSupported_withNonSupportedIdentifier_forModuleProperties() {
- assertWithMessage("DAB frequency identifier radio support in properties")
+ mExpect.withMessage("DAB frequency identifier radio support in properties")
.that(AMFM_PROPERTIES.isProgramIdentifierSupported(
ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY)).isFalse();
}
@Test
public void getDabFrequencyTable_forModulePropertiesInitializedWithNullTable() {
- assertWithMessage("Properties DAB frequency table")
+ mExpect.withMessage("Properties DAB frequency table")
.that(AMFM_PROPERTIES.getDabFrequencyTable()).isNull();
}
@@ -941,32 +944,32 @@
public void getDabFrequencyTable_forModulePropertiesInitializedWithEmptyTable() {
RadioManager.ModuleProperties properties = createAmFmProperties(new ArrayMap<>());
- assertWithMessage("Properties DAB frequency table")
+ mExpect.withMessage("Properties DAB frequency table")
.that(properties.getDabFrequencyTable()).isNull();
}
@Test
public void getVendorInfo_forModuleProperties() {
- assertWithMessage("Properties vendor info")
+ mExpect.withMessage("Properties vendor info")
.that(AMFM_PROPERTIES.getVendorInfo()).isEmpty();
}
@Test
public void getBands_forModuleProperties() {
- assertWithMessage("Properties bands")
+ mExpect.withMessage("Properties bands")
.that(AMFM_PROPERTIES.getBands()).asList()
.containsExactly(AM_BAND_DESCRIPTOR, FM_BAND_DESCRIPTOR);
}
@Test
public void describeContents_forModuleProperties() {
- assertWithMessage("Module properties contents")
+ mExpect.withMessage("Module properties contents")
.that(AMFM_PROPERTIES.describeContents()).isEqualTo(0);
}
@Test
public void toString_forModuleProperties() {
- assertWithMessage("Module properties string").that(AMFM_PROPERTIES.toString())
+ mExpect.withMessage("Module properties string").that(AMFM_PROPERTIES.toString())
.contains(AM_BAND_DESCRIPTOR.toString() + ", " + FM_BAND_DESCRIPTOR.toString());
}
@@ -979,7 +982,7 @@
RadioManager.ModuleProperties modulePropertiesFromParcel =
RadioManager.ModuleProperties.CREATOR.createFromParcel(parcel);
- assertWithMessage("Module properties created from parcel")
+ mExpect.withMessage("Module properties created from parcel")
.that(modulePropertiesFromParcel).isEqualTo(AMFM_PROPERTIES);
}
@@ -994,7 +997,7 @@
RadioManager.ModuleProperties modulePropertiesFromParcel =
RadioManager.ModuleProperties.CREATOR.createFromParcel(parcel);
- assertWithMessage("Module properties created from parcel")
+ mExpect.withMessage("Module properties created from parcel")
.that(modulePropertiesFromParcel).isEqualTo(propertiesToParcel);
}
@@ -1003,7 +1006,7 @@
RadioManager.ModuleProperties propertiesCompared =
createAmFmProperties(/* dabFrequencyTable= */ null);
- assertWithMessage("The same module properties")
+ mExpect.withMessage("The same module properties")
.that(AMFM_PROPERTIES).isEqualTo(propertiesCompared);
}
@@ -1016,7 +1019,7 @@
SUPPORTED_PROGRAM_TYPES, SUPPORTED_IDENTIFIERS_TYPES, Map.of("5A", 174928),
/* vendorInfo= */ null);
- assertWithMessage("Module properties of different id")
+ mExpect.withMessage("Module properties of different id")
.that(AMFM_PROPERTIES).isNotEqualTo(propertiesDab);
}
@@ -1025,7 +1028,7 @@
RadioManager.ModuleProperties propertiesCompared =
createAmFmProperties(/* dabFrequencyTable= */ null);
- assertWithMessage("Hash code of the same module properties")
+ mExpect.withMessage("Hash code of the same module properties")
.that(propertiesCompared.hashCode()).isEqualTo(AMFM_PROPERTIES.hashCode());
}
@@ -1034,86 +1037,86 @@
RadioManager.ModuleProperties[] modulePropertiesArray =
RadioManager.ModuleProperties.CREATOR.newArray(CREATOR_ARRAY_SIZE);
- assertWithMessage("Module properties array")
+ mExpect.withMessage("Module properties array")
.that(modulePropertiesArray).hasLength(CREATOR_ARRAY_SIZE);
}
@Test
public void getSelector_forProgramInfo() {
- assertWithMessage("Selector of DAB program info")
+ mExpect.withMessage("Selector of DAB program info")
.that(DAB_PROGRAM_INFO.getSelector()).isEqualTo(DAB_SELECTOR);
}
@Test
public void getLogicallyTunedTo_forProgramInfo() {
- assertWithMessage("Identifier logically tuned to in DAB program info")
+ mExpect.withMessage("Identifier logically tuned to in DAB program info")
.that(DAB_PROGRAM_INFO.getLogicallyTunedTo()).isEqualTo(DAB_SID_EXT_IDENTIFIER);
}
@Test
public void getPhysicallyTunedTo_forProgramInfo() {
- assertWithMessage("Identifier physically tuned to DAB program info")
+ mExpect.withMessage("Identifier physically tuned to DAB program info")
.that(DAB_PROGRAM_INFO.getPhysicallyTunedTo()).isEqualTo(DAB_FREQUENCY_IDENTIFIER);
}
@Test
public void getRelatedContent_forProgramInfo() {
- assertWithMessage("DAB program info contents")
+ mExpect.withMessage("DAB program info contents")
.that(DAB_PROGRAM_INFO.getRelatedContent())
.containsExactly(DAB_SID_EXT_IDENTIFIER_RELATED);
}
@Test
public void getChannel_forProgramInfo() {
- assertWithMessage("Main channel of DAB program info")
+ mExpect.withMessage("Main channel of DAB program info")
.that(DAB_PROGRAM_INFO.getChannel()).isEqualTo(0);
}
@Test
public void getSubChannel_forProgramInfo() {
- assertWithMessage("Sub channel of DAB program info")
+ mExpect.withMessage("Sub channel of DAB program info")
.that(DAB_PROGRAM_INFO.getSubChannel()).isEqualTo(0);
}
@Test
public void isTuned_forProgramInfo() {
- assertWithMessage("Tuned status of DAB program info")
+ mExpect.withMessage("Tuned status of DAB program info")
.that(DAB_PROGRAM_INFO.isTuned()).isTrue();
}
@Test
public void isStereo_forProgramInfo() {
- assertWithMessage("Stereo support in DAB program info")
+ mExpect.withMessage("Stereo support in DAB program info")
.that(DAB_PROGRAM_INFO.isStereo()).isTrue();
}
@Test
public void isDigital_forProgramInfo() {
- assertWithMessage("Digital DAB program info")
+ mExpect.withMessage("Digital DAB program info")
.that(DAB_PROGRAM_INFO.isDigital()).isTrue();
}
@Test
public void isLive_forProgramInfo() {
- assertWithMessage("Live status of DAB program info")
+ mExpect.withMessage("Live status of DAB program info")
.that(DAB_PROGRAM_INFO.isLive()).isTrue();
}
@Test
public void isMuted_forProgramInfo() {
- assertWithMessage("Muted status of DAB program info")
+ mExpect.withMessage("Muted status of DAB program info")
.that(DAB_PROGRAM_INFO.isMuted()).isFalse();
}
@Test
public void isTrafficProgram_forProgramInfo() {
- assertWithMessage("Traffic program support in DAB program info")
+ mExpect.withMessage("Traffic program support in DAB program info")
.that(DAB_PROGRAM_INFO.isTrafficProgram()).isFalse();
}
@Test
public void isTrafficAnnouncementActive_forProgramInfo() {
- assertWithMessage("Active traffic announcement for DAB program info")
+ mExpect.withMessage("Active traffic announcement for DAB program info")
.that(DAB_PROGRAM_INFO.isTrafficAnnouncementActive()).isFalse();
}
@@ -1121,7 +1124,7 @@
public void isSignalAcquired_forProgramInfo() {
mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
- assertWithMessage("Signal acquisition status for HD program info")
+ mExpect.withMessage("Signal acquisition status for HD program info")
.that(HD_PROGRAM_INFO.isSignalAcquired()).isTrue();
}
@@ -1129,7 +1132,7 @@
public void isHdSisAvailable_forProgramInfo() {
mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
- assertWithMessage("SIS information acquisition status for HD program")
+ mExpect.withMessage("SIS information acquisition status for HD program")
.that(HD_PROGRAM_INFO.isHdSisAvailable()).isTrue();
}
@@ -1137,31 +1140,31 @@
public void isHdAudioAvailable_forProgramInfo() {
mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
- assertWithMessage("Audio acquisition status for HD program")
+ mExpect.withMessage("Audio acquisition status for HD program")
.that(HD_PROGRAM_INFO.isHdAudioAvailable()).isFalse();
}
@Test
public void getSignalStrength_forProgramInfo() {
- assertWithMessage("Signal strength of DAB program info")
+ mExpect.withMessage("Signal strength of DAB program info")
.that(DAB_PROGRAM_INFO.getSignalStrength()).isEqualTo(SIGNAL_QUALITY);
}
@Test
public void getMetadata_forProgramInfo() {
- assertWithMessage("Metadata of DAB program info")
+ mExpect.withMessage("Metadata of DAB program info")
.that(DAB_PROGRAM_INFO.getMetadata()).isEqualTo(METADATA);
}
@Test
public void getVendorInfo_forProgramInfo() {
- assertWithMessage("Vendor info of DAB program info")
+ mExpect.withMessage("Vendor info of DAB program info")
.that(DAB_PROGRAM_INFO.getVendorInfo()).isEmpty();
}
@Test
public void describeContents_forProgramInfo() {
- assertWithMessage("Program info contents")
+ mExpect.withMessage("Program info contents")
.that(DAB_PROGRAM_INFO.describeContents()).isEqualTo(0);
}
@@ -1170,7 +1173,7 @@
RadioManager.ProgramInfo[] programInfoArray =
RadioManager.ProgramInfo.CREATOR.newArray(CREATOR_ARRAY_SIZE);
- assertWithMessage("Program infos").that(programInfoArray).hasLength(CREATOR_ARRAY_SIZE);
+ mExpect.withMessage("Program infos").that(programInfoArray).hasLength(CREATOR_ARRAY_SIZE);
}
@Test
@@ -1182,7 +1185,7 @@
RadioManager.ProgramInfo programInfoFromParcel =
RadioManager.ProgramInfo.CREATOR.createFromParcel(parcel);
- assertWithMessage("Program info created from parcel")
+ mExpect.withMessage("Program info created from parcel")
.that(programInfoFromParcel).isEqualTo(DAB_PROGRAM_INFO);
}
@@ -1190,7 +1193,7 @@
public void equals_withSameProgramInfo_returnsTrue() {
RadioManager.ProgramInfo dabProgramInfoCompared = createDabProgramInfo(DAB_SELECTOR);
- assertWithMessage("The same program info")
+ mExpect.withMessage("The same program info")
.that(dabProgramInfoCompared).isEqualTo(DAB_PROGRAM_INFO);
}
@@ -1202,7 +1205,7 @@
/* vendorIds= */ null);
RadioManager.ProgramInfo dabProgramInfoCompared = createDabProgramInfo(dabSelectorCompared);
- assertWithMessage("Program info with different secondary id selectors")
+ mExpect.withMessage("Program info with different secondary id selectors")
.that(DAB_PROGRAM_INFO).isNotEqualTo(dabProgramInfoCompared);
}
@@ -1213,7 +1216,7 @@
mRadioManager.listModules(modules);
- assertWithMessage("Modules in radio manager")
+ mExpect.withMessage("Modules in radio manager")
.that(modules).containsExactly(AMFM_PROPERTIES);
}
@@ -1221,7 +1224,7 @@
public void listModules_forRadioManagerWithNullListAsInput_fails() throws Exception {
createRadioManager();
- assertWithMessage("Status when listing module with empty list input")
+ mExpect.withMessage("Status when listing module with empty list input")
.that(mRadioManager.listModules(null)).isEqualTo(RadioManager.STATUS_BAD_VALUE);
}
@@ -1231,7 +1234,7 @@
when(mRadioServiceMock.listModules()).thenReturn(null);
List<RadioManager.ModuleProperties> modules = new ArrayList<>();
- assertWithMessage("Status for listing module when getting null list from HAL client")
+ mExpect.withMessage("Status for listing module when getting null list from HAL client")
.that(mRadioManager.listModules(modules)).isEqualTo(RadioManager.STATUS_ERROR);
}
@@ -1241,7 +1244,7 @@
when(mRadioServiceMock.listModules()).thenThrow(new RemoteException());
List<RadioManager.ModuleProperties> modules = new ArrayList<>();
- assertWithMessage("Status for listing module when HAL client service is dead")
+ mExpect.withMessage("Status for listing module when HAL client service is dead")
.that(mRadioManager.listModules(modules))
.isEqualTo(RadioManager.STATUS_DEAD_OBJECT);
}
@@ -1267,7 +1270,21 @@
RadioTuner nullTuner = mRadioManager.openTuner(/* moduleId= */ 0, FM_BAND_CONFIG,
/* withAudio= */ true, mCallbackMock, /* handler= */ null);
- assertWithMessage("Radio tuner when service is dead").that(nullTuner).isNull();
+ mExpect.withMessage("Radio tuner when service is dead").that(nullTuner).isNull();
+ }
+
+ @Test
+ public void openTuner_withNullCallback() throws Exception {
+ createRadioManager();
+ int moduleId = 0;
+ boolean withAudio = true;
+
+ IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
+ () -> mRadioManager.openTuner(moduleId, FM_BAND_CONFIG, withAudio,
+ /* callback= */ null, /* handler= */ null));
+
+ mExpect.withMessage("Null tuner callback exception").that(thrown)
+ .hasMessageThat().contains("callback must not be empty");
}
@Test
@@ -1323,7 +1340,7 @@
RuntimeException thrown = assertThrows(RuntimeException.class,
() -> mRadioManager.addAnnouncementListener(enableTypeSet, mEventListener));
- assertWithMessage("Exception for adding announcement listener with dead service")
+ mExpect.withMessage("Exception for adding announcement listener with dead service")
.that(thrown).hasMessageThat().contains(exceptionMessage);
}
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java
index 0e0dbec..2bf0aa3 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java
@@ -16,11 +16,11 @@
package com.android.server.broadcastradio.aidl;
-import static com.google.common.truth.Truth.assertWithMessage;
-
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -33,7 +33,10 @@
import android.os.IBinder;
import android.os.RemoteException;
+import com.google.common.truth.Expect;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -54,6 +57,9 @@
private AnnouncementAggregator mAnnouncementAggregator;
private IBinder.DeathRecipient mDeathRecipient;
+ @Rule
+ public final Expect mExpect = Expect.create();
+
@Mock
private IAnnouncementListener mListenerMock;
@Mock
@@ -75,6 +81,18 @@
}
@Test
+ public void constructor_withBinderDied() throws Exception {
+ RemoteException remoteException = new RemoteException("Binder is died");
+ doThrow(remoteException).when(mBinderMock).linkToDeath(any(), anyInt());
+
+ RuntimeException thrown = assertThrows(RuntimeException.class, () ->
+ new AnnouncementAggregator(mListenerMock, mLock));
+
+ mExpect.withMessage("Exception for dead binder").that(thrown).hasMessageThat()
+ .contains(remoteException.getMessage());
+ }
+
+ @Test
public void onListUpdated_withOneModuleWatcher() throws Exception {
ArgumentCaptor<IAnnouncementListener> moduleWatcherCaptor =
ArgumentCaptor.forClass(IAnnouncementListener.class);
@@ -103,7 +121,7 @@
moduleWatcherCaptor.getValue().onListUpdated(Arrays.asList(mAnnouncementMocks[index]));
verify(mListenerMock, times(index + 1)).onListUpdated(announcementsCaptor.capture());
- assertWithMessage("Number of announcements %s after %s announcements were updated",
+ mExpect.withMessage("Number of announcements %s after %s announcements were updated",
announcementsCaptor.getValue(), index + 1)
.that(announcementsCaptor.getValue().size()).isEqualTo(index + 1);
}
@@ -131,7 +149,7 @@
() -> mAnnouncementAggregator.watchModule(mRadioModuleMocks[0],
TEST_ENABLED_TYPES));
- assertWithMessage("Exception for watching module after aggregator has been closed")
+ mExpect.withMessage("Exception for watching module after aggregator has been closed")
.that(thrown).hasMessageThat()
.contains("announcement aggregator has already been closed");
}
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
index 8d9fad9..42501c1 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
@@ -27,6 +27,7 @@
import android.hardware.broadcastradio.DabTableEntry;
import android.hardware.broadcastradio.IdentifierType;
import android.hardware.broadcastradio.Metadata;
+import android.hardware.broadcastradio.ProgramFilter;
import android.hardware.broadcastradio.ProgramIdentifier;
import android.hardware.broadcastradio.ProgramInfo;
import android.hardware.broadcastradio.Properties;
@@ -41,6 +42,7 @@
import android.hardware.radio.UniqueProgramIdentifier;
import android.os.ServiceSpecificException;
import android.platform.test.flag.junit.SetFlagsRule;
+import android.util.ArraySet;
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase;
@@ -93,6 +95,11 @@
private static final long TEST_HD_LOCATION_VALUE = 0x4E647007665CF6L;
private static final long TEST_VENDOR_ID_VALUE = 9_901;
+ private static final ProgramSelector.Identifier TEST_INVALID_ID =
+ new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_INVALID, 1);
+ private static final ProgramIdentifier TEST_HAL_INVALID_ID =
+ AidlTestUtils.makeHalIdentifier(IdentifierType.INVALID, 1);
+
private static final ProgramSelector.Identifier TEST_DAB_SID_EXT_ID =
new ProgramSelector.Identifier(
ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT, TEST_DAB_DMB_SID_EXT_VALUE);
@@ -139,7 +146,7 @@
private static final int TEST_ANNOUNCEMENT_FREQUENCY = FM_LOWER_LIMIT + FM_SPACING;
private static final RadioManager.ModuleProperties MODULE_PROPERTIES =
- convertToModuleProperties();
+ createModuleProperties();
private static final Announcement ANNOUNCEMENT =
ConversionUtils.announcementFromHalAnnouncement(
AidlTestUtils.makeAnnouncement(TEST_ENABLED_TYPE, TEST_ANNOUNCEMENT_FREQUENCY));
@@ -291,6 +298,37 @@
}
@Test
+ public void propertiesFromHalProperties_withoutAmFmAndDabConfigs() {
+ RadioManager.ModuleProperties properties = createModuleProperties(/* amFmConfig= */ null,
+ new DabTableEntry[]{});
+
+ expect.withMessage("Empty AM/FM config")
+ .that(properties.getBands()).asList().isEmpty();
+ expect.withMessage("Empty DAB config")
+ .that(properties.getDabFrequencyTable()).isNull();
+ }
+
+ @Test
+ public void propertiesFromHalProperties_withInvalidBand() {
+ AmFmRegionConfig amFmRegionConfig = new AmFmRegionConfig();
+ amFmRegionConfig.ranges = new AmFmBandRange[]{createAmFmBandRange(/* lowerBound= */ 50000,
+ /* upperBound= */ 60000, /* spacing= */ 10),
+ createAmFmBandRange(FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING)};
+
+ RadioManager.ModuleProperties properties = createModuleProperties(amFmRegionConfig,
+ new DabTableEntry[]{});
+
+ RadioManager.BandDescriptor[] bands = properties.getBands();
+ expect.withMessage("Band descriptors").that(bands).hasLength(1);
+ expect.withMessage("FM band frequency lower limit")
+ .that(bands[0].getLowerLimit()).isEqualTo(FM_LOWER_LIMIT);
+ expect.withMessage("FM band frequency upper limit")
+ .that(bands[0].getUpperLimit()).isEqualTo(FM_UPPER_LIMIT);
+ expect.withMessage("FM band frequency spacing")
+ .that(bands[0].getSpacing()).isEqualTo(FM_SPACING);
+ }
+
+ @Test
public void identifierToHalProgramIdentifier_withDabId() {
ProgramIdentifier halDabId =
ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_SID_EXT_ID);
@@ -358,6 +396,13 @@
}
@Test
+ public void identifierFromHalProgramIdentifier_withInvalidIdentifier() {
+ expect.withMessage("Identifier converted from invalid HAL identifier")
+ .that(ConversionUtils.identifierFromHalProgramIdentifier(TEST_HAL_INVALID_ID))
+ .isNull();
+ }
+
+ @Test
public void programSelectorToHalProgramSelector_withValidSelector() {
android.hardware.broadcastradio.ProgramSelector halDabSelector =
ConversionUtils.programSelectorToHalProgramSelector(TEST_DAB_SELECTOR);
@@ -370,6 +415,23 @@
}
@Test
+ public void programSelectorToHalProgramSelector_withInvalidSecondaryId() {
+ ProgramSelector dabSelector = new ProgramSelector(ProgramSelector.PROGRAM_TYPE_DAB,
+ TEST_DAB_SID_EXT_ID, new ProgramSelector.Identifier[]{TEST_INVALID_ID,
+ TEST_DAB_FREQUENCY_ID, TEST_DAB_ENSEMBLE_ID}, /* vendorIds= */ null);
+
+ android.hardware.broadcastradio.ProgramSelector halDabSelector =
+ ConversionUtils.programSelectorToHalProgramSelector(dabSelector);
+
+ expect.withMessage("Primary identifier of converted HAL DAB selector with invalid "
+ + "secondary id").that(halDabSelector.primaryId)
+ .isEqualTo(TEST_HAL_DAB_SID_EXT_ID);
+ expect.withMessage("Secondary identifiers of converted HAL DAB selector with "
+ + "invalid secondary id").that(halDabSelector.secondaryIds).asList()
+ .containsExactly(TEST_HAL_DAB_FREQUENCY_ID, TEST_HAL_DAB_ENSEMBLE_ID);
+ }
+
+ @Test
public void programSelectorFromHalProgramSelector_withValidSelector() {
android.hardware.broadcastradio.ProgramSelector halDabSelector =
AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{
@@ -386,6 +448,33 @@
}
@Test
+ public void programSelectorFromHalProgramSelector_withInvalidSelector() {
+ android.hardware.broadcastradio.ProgramSelector invalidSelector =
+ AidlTestUtils.makeHalSelector(TEST_HAL_INVALID_ID, new ProgramIdentifier[]{});
+
+ expect.withMessage("Selector converted from invalid HAL selector")
+ .that(ConversionUtils.programSelectorFromHalProgramSelector(invalidSelector))
+ .isNull();
+ }
+
+ @Test
+ public void programSelectorFromHalProgramSelector_withInvalidSecondaryId() {
+ android.hardware.broadcastradio.ProgramSelector halDabSelector =
+ AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{
+ TEST_HAL_INVALID_ID, TEST_HAL_DAB_ENSEMBLE_ID, TEST_HAL_DAB_FREQUENCY_ID});
+
+ ProgramSelector dabSelector =
+ ConversionUtils.programSelectorFromHalProgramSelector(halDabSelector);
+
+ expect.withMessage("Primary identifier of converted DAB selector with invalid "
+ + "secondary id").that(dabSelector.getPrimaryId())
+ .isEqualTo(TEST_DAB_SID_EXT_ID);
+ expect.withMessage("Secondary identifiers of converted DAB selector with invalid "
+ + "secondary id").that(dabSelector.getSecondaryIds()).asList()
+ .containsExactly(TEST_DAB_FREQUENCY_ID, TEST_DAB_ENSEMBLE_ID);
+ }
+
+ @Test
public void programInfoFromHalProgramInfo_withValidProgramInfo() {
android.hardware.broadcastradio.ProgramSelector halDabSelector =
AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{
@@ -622,11 +711,47 @@
.isEqualTo(TEST_ALBUM_ART);
}
- private static RadioManager.ModuleProperties convertToModuleProperties() {
+ @Test
+ public void getBands_withInvalidFrequency() {
+ expect.withMessage("Band for invalid frequency")
+ .that(Utils.getBand(/* freq= */ 110000)).isEqualTo(Utils.FrequencyBand.UNKNOWN);
+ }
+
+ @Test
+ public void filterToHalProgramFilter_withNullFilter() {
+ ProgramFilter filter = ConversionUtils.filterToHalProgramFilter(null);
+
+ expect.withMessage("Filter identifier types").that(filter.identifierTypes)
+ .asList().isEmpty();
+ expect.withMessage("Filter identifiers").that(filter.identifiers).asList()
+ .isEmpty();
+ }
+
+ @Test
+ public void filterToHalProgramFilter_withInvalidIdentifier() {
+ Set<ProgramSelector.Identifier> identifiers =
+ new ArraySet<ProgramSelector.Identifier>(2);
+ identifiers.add(TEST_INVALID_ID);
+ identifiers.add(TEST_DAB_SID_EXT_ID);
+ ProgramList.Filter filter = new ProgramList.Filter(/* identifierTypes */ new ArraySet<>(),
+ identifiers, /* includeCategories= */ true, /* excludeModifications= */ false);
+ ProgramFilter halFilter = ConversionUtils.filterToHalProgramFilter(filter);
+
+ expect.withMessage("Filter identifiers with invalid ones removed")
+ .that(halFilter.identifiers).asList().containsExactly(
+ ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_SID_EXT_ID));
+ }
+
+ private static RadioManager.ModuleProperties createModuleProperties() {
AmFmRegionConfig amFmConfig = createAmFmRegionConfig();
DabTableEntry[] dabTableEntries = new DabTableEntry[]{
createDabTableEntry(DAB_ENTRY_LABEL_1, DAB_ENTRY_FREQUENCY_1),
createDabTableEntry(DAB_ENTRY_LABEL_2, DAB_ENTRY_FREQUENCY_2)};
+ return createModuleProperties(amFmConfig, dabTableEntries);
+ }
+
+ private static RadioManager.ModuleProperties createModuleProperties(
+ AmFmRegionConfig amFmConfig, DabTableEntry[] dabTableEntries) {
Properties properties = createHalProperties();
return ConversionUtils.propertiesFromHalProperties(TEST_ID, TEST_SERVICE_NAME, properties,
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java
index ce27bc1..d64fcaf 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java
@@ -440,6 +440,29 @@
TEST_DAB_UNIQUE_ID_ALTERNATIVE);
}
+ @Test
+ public void filterAndApplyChunkInternal_withInvalidProgramInfoAndIdentifiers()
+ throws RemoteException {
+ ProgramInfoCache cache = new ProgramInfoCache(/* filter= */ null,
+ /* complete= */ false, TEST_FM_INFO, TEST_RDS_INFO, TEST_DAB_INFO);
+ ProgramInfo[] halModified = new android.hardware.broadcastradio.ProgramInfo[1];
+ halModified[0] = AidlTestUtils.makeHalProgramInfo(
+ ConversionUtils.programSelectorToHalProgramSelector(TEST_DAB_SELECTOR_ALTERNATIVE),
+ ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_FREQUENCY_ID_ALTERNATIVE),
+ ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_FREQUENCY_ID_ALTERNATIVE),
+ TEST_SIGNAL_QUALITY);
+ ProgramIdentifier[] halRemoved = new android.hardware.broadcastradio.ProgramIdentifier[1];
+ halRemoved[0] = new android.hardware.broadcastradio.ProgramIdentifier();
+ ProgramListChunk halChunk = AidlTestUtils.makeHalChunk(/* purge= */ false,
+ /* complete= */ true, halModified, halRemoved);
+
+ List<ProgramList.Chunk> programListChunks = cache.filterAndApplyChunkInternal(halChunk,
+ TEST_MAX_NUM_MODIFIED_PER_CHUNK, TEST_MAX_NUM_REMOVED_PER_CHUNK);
+
+ expect.withMessage("Program list chunk applied with invalid program and identifiers")
+ .that(programListChunks).isEmpty();
+ }
+
private void verifyChunkListPurge(List<ProgramList.Chunk> chunks, boolean purge) {
if (chunks.isEmpty()) {
return;
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java
index 10ac05d..a952bde 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java
@@ -16,13 +16,12 @@
package com.android.server.broadcastradio.aidl;
-import static com.google.common.truth.Truth.assertWithMessage;
-
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -32,9 +31,13 @@
import android.hardware.radio.IAnnouncementListener;
import android.hardware.radio.ICloseHandle;
import android.hardware.radio.RadioManager;
+import android.os.ParcelableException;
import android.os.RemoteException;
+import com.google.common.truth.Expect;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -50,6 +53,9 @@
private static final RadioManager.ModuleProperties TEST_MODULE_PROPERTIES =
AidlTestUtils.makeDefaultModuleProperties();
+ @Rule
+ public final Expect mExpect = Expect.create();
+
// Mocks
@Mock
private IBroadcastRadio mBroadcastRadioMock;
@@ -77,13 +83,13 @@
@Test
public void getService() {
- assertWithMessage("Service of radio module")
+ mExpect.withMessage("Service of radio module")
.that(mRadioModule.getService()).isEqualTo(mBroadcastRadioMock);
}
@Test
public void getProperties() {
- assertWithMessage("Module properties of radio module")
+ mExpect.withMessage("Module properties of radio module")
.that(mRadioModule.getProperties()).isEqualTo(TEST_MODULE_PROPERTIES);
}
@@ -93,7 +99,7 @@
Bitmap imageTest = mRadioModule.getImage(imageId);
- assertWithMessage("Image from radio module").that(imageTest).isNull();
+ mExpect.withMessage("Image from radio module").that(imageTest).isNull();
}
@Test
@@ -104,7 +110,7 @@
mRadioModule.getImage(invalidImageId);
});
- assertWithMessage("Exception for getting image with invalid ID")
+ mExpect.withMessage("Exception for getting image with invalid ID")
.that(thrown).hasMessageThat().contains("Image ID is missing");
}
@@ -117,6 +123,18 @@
}
@Test
+ public void addAnnouncementListener_whenHalThrowsRemoteException() throws Exception {
+ doThrow(new RuntimeException("HAL service died")).when(mBroadcastRadioMock)
+ .registerAnnouncementListener(any(), any());
+
+ ParcelableException thrown = assertThrows(ParcelableException.class, () ->
+ mRadioModule.addAnnouncementListener(mListenerMock, new int[]{TEST_ENABLED_TYPE}));
+
+ mExpect.withMessage("Exception for adding announcement listener when HAL service died")
+ .that(thrown).hasMessageThat().contains("unknown error");
+ }
+
+ @Test
public void onListUpdate_forAnnouncementListener() throws Exception {
android.hardware.broadcastradio.Announcement halAnnouncement =
AidlTestUtils.makeAnnouncement(TEST_ENABLED_TYPE, /* selectorFreq= */ 96300);
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
index 755bcdb..4ded91d 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
@@ -421,6 +421,19 @@
}
@Test
+ public void tune_withClosedTuner_fails() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ ProgramSelector sel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
+ mTunerSessions[0].close();
+
+ IllegalStateException thrown = assertThrows(IllegalStateException.class,
+ () -> mTunerSessions[0].tune(sel));
+
+ expect.withMessage("Exception for tuning on closed tuner").that(thrown).hasMessageThat()
+ .contains("Tuner is closed");
+ }
+
+ @Test
public void step_withDirectionUp() throws Exception {
long initFreq = AM_FM_FREQUENCY_LIST[1];
ProgramSelector initialSel = AidlTestUtils.makeFmSelector(initFreq);
@@ -1149,6 +1162,20 @@
}
@Test
+ public void onCurrentProgramInfoChanged_withLowerSdkVersion_doesNotInvokesCallback()
+ throws Exception {
+ doReturn(false).when(() -> CompatChanges.isChangeEnabled(
+ eq(ConversionUtils.RADIO_U_VERSION_REQUIRED), anyInt()));
+ openAidlClients(/* numClients= */ 1);
+
+ mHalTunerCallback.onCurrentProgramInfoChanged(
+ AidlTestUtils.programInfoToHalProgramInfo(TEST_DAB_INFO));
+
+ verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).never())
+ .onCurrentProgramInfoChanged(any());
+ }
+
+ @Test
public void onTuneFailed_forTunerCallback() throws Exception {
int numSessions = 3;
openAidlClients(numSessions);
@@ -1165,6 +1192,20 @@
}
@Test
+ public void onTuneFailed_withLowerSdkVersion_doesNotInvokesCallback()
+ throws Exception {
+ doReturn(false).when(() -> CompatChanges.isChangeEnabled(
+ eq(ConversionUtils.RADIO_U_VERSION_REQUIRED), anyInt()));
+ openAidlClients(/* numClients= */ 1);
+
+ mHalTunerCallback.onTuneFailed(Result.CANCELED,
+ ConversionUtils.programSelectorToHalProgramSelector(TEST_DAB_SELECTOR));
+
+ verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).never())
+ .onTuneFailed(anyInt(), any());
+ }
+
+ @Test
public void onAntennaStateChange_forTunerCallback() throws Exception {
int numSessions = 3;
openAidlClients(numSessions);
@@ -1231,6 +1272,36 @@
}
}
+ @Test
+ public void openSession_withNonNullAntennaState() throws Exception {
+ boolean antennaConnected = false;
+ android.hardware.radio.ITunerCallback callback =
+ mock(android.hardware.radio.ITunerCallback.class);
+ openAidlClients(/* numClients= */ 1);
+ mHalTunerCallback.onAntennaStateChange(antennaConnected);
+ verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onAntennaState(antennaConnected);
+
+ mRadioModule.openSession(callback);
+
+ verify(callback, CALLBACK_TIMEOUT).onAntennaState(antennaConnected);
+ }
+
+ @Test
+ public void openSession_withNonNullCurrentProgramInfo() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
+ RadioManager.ProgramInfo tuneInfo = AidlTestUtils.makeProgramInfo(initialSel,
+ SIGNAL_QUALITY);
+ mTunerSessions[0].tune(initialSel);
+ verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo);
+ android.hardware.radio.ITunerCallback callback =
+ mock(android.hardware.radio.ITunerCallback.class);
+
+ mRadioModule.openSession(callback);
+
+ verify(callback, CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo);
+ }
+
private void openAidlClients(int numClients) throws Exception {
mAidlTunerCallbackMocks = new android.hardware.radio.ITunerCallback[numClients];
mTunerSessions = new TunerSession[numClients];
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java
index 5e99b28..8e0abff 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java
@@ -16,11 +16,11 @@
package com.android.server.broadcastradio.hal2;
-import static com.google.common.truth.Truth.assertWithMessage;
-
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -33,7 +33,10 @@
import android.os.IBinder;
import android.os.RemoteException;
+import com.google.common.truth.Expect;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -55,6 +58,9 @@
private AnnouncementAggregator mAnnouncementAggregator;
private IBinder.DeathRecipient mDeathRecipient;
+ @Rule
+ public final Expect mExpect = Expect.create();
+
@Mock
private IAnnouncementListener mListenerMock;
@Mock
@@ -76,6 +82,19 @@
}
@Test
+ public void constructor_withBinderDied() throws Exception {
+ RemoteException remoteException = new RemoteException("Binder is died");
+ doThrow(remoteException).when(mBinderMock).linkToDeath(any(), anyInt());
+
+ RuntimeException thrown = assertThrows(RuntimeException.class,
+ () -> new com.android.server.broadcastradio.aidl.AnnouncementAggregator(
+ mListenerMock, mLock));
+
+ mExpect.withMessage("Exception for dead binder").that(thrown).hasMessageThat()
+ .contains(remoteException.getMessage());
+ }
+
+ @Test
public void onListUpdated_withOneModuleWatcher() throws Exception {
ArgumentCaptor<IAnnouncementListener> moduleWatcherCaptor =
ArgumentCaptor.forClass(IAnnouncementListener.class);
@@ -104,7 +123,7 @@
moduleWatcherCaptor.getValue().onListUpdated(Arrays.asList(mAnnouncementMocks[index]));
verify(mListenerMock, times(index + 1)).onListUpdated(announcementsCaptor.capture());
- assertWithMessage("Number of announcements %s after %s announcements were updated",
+ mExpect.withMessage("Number of announcements %s after %s announcements were updated",
announcementsCaptor.getValue(), index + 1)
.that(announcementsCaptor.getValue().size()).isEqualTo(index + 1);
}
@@ -132,7 +151,7 @@
() -> mAnnouncementAggregator.watchModule(mRadioModuleMocks[0],
TEST_ENABLED_TYPES));
- assertWithMessage("Exception for watching module after aggregator has been closed")
+ mExpect.withMessage("Exception for watching module after aggregator has been closed")
.that(thrown).hasMessageThat()
.contains("announcement aggregator has already been closed");
}
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java
index 3de4f5d..4cb012c 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java
@@ -21,7 +21,6 @@
import android.hardware.broadcastradio.V2_0.DabTableEntry;
import android.hardware.broadcastradio.V2_0.IdentifierType;
import android.hardware.broadcastradio.V2_0.Properties;
-import android.hardware.broadcastradio.V2_0.VendorKeyValue;
import android.hardware.radio.Announcement;
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
@@ -149,6 +148,26 @@
}
@Test
+ public void propertiesFromHalProperties_withInvalidBand() {
+ AmFmRegionConfig amFmRegionConfig = new AmFmRegionConfig();
+ amFmRegionConfig.ranges = new ArrayList<>(Arrays.asList(createAmFmBandRange(
+ /* lowerBound= */ 50000, /* upperBound= */ 60000, /* spacing= */ 10),
+ createAmFmBandRange(FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING)));
+
+ RadioManager.ModuleProperties properties = convertToModuleProperties(amFmRegionConfig,
+ new ArrayList<>());
+
+ RadioManager.BandDescriptor[] bands = properties.getBands();
+ expect.withMessage("Band descriptors").that(bands).hasLength(1);
+ expect.withMessage("FM band frequency lower limit")
+ .that(bands[0].getLowerLimit()).isEqualTo(FM_LOWER_LIMIT);
+ expect.withMessage("FM band frequency upper limit")
+ .that(bands[0].getUpperLimit()).isEqualTo(FM_UPPER_LIMIT);
+ expect.withMessage("FM band frequency spacing")
+ .that(bands[0].getSpacing()).isEqualTo(FM_SPACING);
+ }
+
+ @Test
public void announcementFromHalAnnouncement_typesMatch() {
expect.withMessage("Announcement type")
.that(ANNOUNCEMENT.getType()).isEqualTo(TEST_ENABLED_TYPE);
@@ -173,20 +192,31 @@
.that(ANNOUNCEMENT.getVendorInfo()).isEmpty();
}
+ @Test
+ public void getBands_withInvalidFrequency() {
+ expect.withMessage("Band for invalid frequency")
+ .that(Utils.getBand(/* freq= */ 110000)).isEqualTo(FrequencyBand.UNKNOWN);
+ }
+
private static RadioManager.ModuleProperties convertToModuleProperties() {
AmFmRegionConfig amFmConfig = createAmFmRegionConfig();
List<DabTableEntry> dabTableEntries = Arrays.asList(
createDabTableEntry(DAB_ENTRY_LABEL_1, DAB_ENTRY_FREQUENCY_1),
createDabTableEntry(DAB_ENTRY_LABEL_2, DAB_ENTRY_FREQUENCY_2));
- Properties properties = createHalProperties();
+ return convertToModuleProperties(amFmConfig, dabTableEntries);
+ }
+
+ private static RadioManager.ModuleProperties convertToModuleProperties(
+ AmFmRegionConfig amFmConfig, List<DabTableEntry> dabTableEntries) {
+ Properties properties = createHalProperties();
return Convert.propertiesFromHal(TEST_ID, TEST_SERVICE_NAME, properties,
amFmConfig, dabTableEntries);
}
private static AmFmRegionConfig createAmFmRegionConfig() {
AmFmRegionConfig amFmRegionConfig = new AmFmRegionConfig();
- amFmRegionConfig.ranges = new ArrayList<AmFmBandRange>(Arrays.asList(
+ amFmRegionConfig.ranges = new ArrayList<>(Arrays.asList(
createAmFmBandRange(FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING),
createAmFmBandRange(AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING)));
return amFmRegionConfig;
@@ -216,7 +246,7 @@
halProperties.product = TEST_PRODUCT;
halProperties.version = TEST_VERSION;
halProperties.serial = TEST_SERIAL;
- halProperties.vendorInfo = new ArrayList<VendorKeyValue>(Arrays.asList(
+ halProperties.vendorInfo = new ArrayList<>(Arrays.asList(
TestUtils.makeVendorKeyValue(VENDOR_INFO_KEY_1, VENDOR_INFO_VALUE_1),
TestUtils.makeVendorKeyValue(VENDOR_INFO_KEY_2, VENDOR_INFO_VALUE_2)));
return halProperties;
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java
index 36a6430..015e9c0 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java
@@ -17,6 +17,7 @@
import static org.junit.Assert.*;
+import android.hardware.broadcastradio.V2_0.ProgramIdentifier;
import android.hardware.broadcastradio.V2_0.ProgramListChunk;
import android.hardware.radio.ProgramList;
import android.hardware.radio.ProgramSelector;
@@ -34,6 +35,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -261,6 +263,25 @@
verifyChunkListRemoved(chunks, 1, TEST_DAB_UNIQUE_ID, TEST_VENDOR_UNIQUE_ID);
}
+ @Test
+ public void filterAndApplyChunkInternal_withInvalidIdentifier() {
+ ProgramInfoCache cache = new ProgramInfoCache(/* filter= */ null, /* complete= */ false,
+ TEST_AM_FM_INFO, TEST_RDS_INFO, TEST_DAB_INFO, TEST_VENDOR_INFO);
+ ArrayList<ProgramIdentifier> halRemoved = new ArrayList<>();
+ halRemoved.add(new ProgramIdentifier());
+ ProgramListChunk halChunk = new ProgramListChunk();
+ halChunk.complete = true;
+ halChunk.purge = false;
+ halChunk.modified = new ArrayList<>();
+ halChunk.removed = halRemoved;
+
+ List<ProgramList.Chunk> programListChunks = cache.filterAndApplyChunkInternal(halChunk,
+ /* maxNumModifiedPerChunk= */ 1, /* maxNumRemovedPerChunk= */ 1);
+
+ expect.withMessage("Program list chunk applied with invalid identifier")
+ .that(programListChunks).isEmpty();
+ }
+
// Verifies that:
// - The first chunk's purge flag matches expectPurge.
// - The last chunk's complete flag matches expectComplete.
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
index 6edfa02..898ef57 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
@@ -29,8 +29,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.timeout;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.google.common.truth.Truth.assertWithMessage;
-
import static org.junit.Assert.assertThrows;
import android.graphics.Bitmap;
@@ -57,8 +55,11 @@
import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase;
import com.android.server.broadcastradio.RadioServiceUserController;
+import com.google.common.truth.Expect;
+
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -98,6 +99,9 @@
private ProgramInfo mHalCurrentInfo;
private TunerSession[] mTunerSessions;
+ @Rule
+ public final Expect mExpect = Expect.create();
+
@Mock
private UserHandle mUserHandleMock;
@Mock
@@ -206,7 +210,7 @@
openAidlClients(numSessions);
for (int index = 0; index < numSessions; index++) {
- assertWithMessage("Session of index %s close state", index)
+ mExpect.withMessage("Session of index %s close state", index)
.that(mTunerSessions[index].isClosed()).isFalse();
}
}
@@ -238,7 +242,7 @@
RadioManager.BandConfig config = mTunerSessions[0].getConfiguration();
- assertWithMessage("Session configuration").that(config)
+ mExpect.withMessage("Session configuration").that(config)
.isEqualTo(FM_BAND_CONFIG);
}
@@ -248,7 +252,7 @@
mTunerSessions[0].setMuted(/* mute= */ false);
- assertWithMessage("Session mute state after setting unmuted")
+ mExpect.withMessage("Session mute state after setting unmuted")
.that(mTunerSessions[0].isMuted()).isFalse();
}
@@ -258,7 +262,7 @@
mTunerSessions[0].setMuted(/* mute= */ true);
- assertWithMessage("Session mute state after setting muted")
+ mExpect.withMessage("Session mute state after setting muted")
.that(mTunerSessions[0].isMuted()).isTrue();
}
@@ -268,7 +272,7 @@
mTunerSessions[0].close();
- assertWithMessage("Close state of broadcast radio service session")
+ mExpect.withMessage("Close state of broadcast radio service session")
.that(mTunerSessions[0].isClosed()).isTrue();
}
@@ -282,11 +286,11 @@
for (int index = 0; index < numSessions; index++) {
if (index == closeIdx) {
- assertWithMessage(
+ mExpect.withMessage(
"Close state of broadcast radio service session of index %s", index)
.that(mTunerSessions[index].isClosed()).isTrue();
} else {
- assertWithMessage(
+ mExpect.withMessage(
"Close state of broadcast radio service session of index %s", index)
.that(mTunerSessions[index].isClosed()).isFalse();
}
@@ -301,7 +305,21 @@
mTunerSessions[0].close(errorCode);
verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onError(errorCode);
- assertWithMessage("Close state of broadcast radio service session")
+ mExpect.withMessage("Close state of broadcast radio service session")
+ .that(mTunerSessions[0].isClosed()).isTrue();
+ }
+
+ @Test
+ public void close_forMultipleTimes() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ int errorCode = RadioTuner.ERROR_SERVER_DIED;
+ mTunerSessions[0].close(errorCode);
+ verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onError(errorCode);
+
+ mTunerSessions[0].close(errorCode);
+
+ verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onError(errorCode);
+ mExpect.withMessage("State of closing broadcast radio service session twice")
.that(mTunerSessions[0].isClosed()).isTrue();
}
@@ -315,7 +333,7 @@
for (int index = 0; index < numSessions; index++) {
verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT).onError(errorCode);
- assertWithMessage("Close state of broadcast radio service session of index %s", index)
+ mExpect.withMessage("Close state of broadcast radio service session of index %s", index)
.that(mTunerSessions[index].isClosed()).isTrue();
}
}
@@ -365,7 +383,7 @@
UnsupportedOperationException thrown = assertThrows(UnsupportedOperationException.class,
() -> mTunerSessions[0].tune(unsupportedSelector));
- assertWithMessage("Exception for tuning on unsupported program selector")
+ mExpect.withMessage("Exception for tuning on unsupported program selector")
.that(thrown).hasMessageThat().contains("tune: NOT_SUPPORTED");
}
@@ -393,11 +411,24 @@
mTunerSessions[0].tune(sel);
});
- assertWithMessage("Unknown error HAL exception when tuning")
+ mExpect.withMessage("Unknown error HAL exception when tuning")
.that(thrown).hasMessageThat().contains(Result.toString(Result.UNKNOWN_ERROR));
}
@Test
+ public void tune_withClosedTuner_fails() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ ProgramSelector sel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
+ mTunerSessions[0].close();
+
+ IllegalStateException thrown = assertThrows(IllegalStateException.class,
+ () -> mTunerSessions[0].tune(sel));
+
+ mExpect.withMessage("Exception for tuning on closed tuner").that(thrown).hasMessageThat()
+ .contains("Tuner is closed");
+ }
+
+ @Test
public void step_withDirectionUp() throws Exception {
long initFreq = AM_FM_FREQUENCY_LIST[1];
ProgramSelector initialSel = TestUtils.makeFmSelector(initFreq);
@@ -454,7 +485,7 @@
mTunerSessions[0].step(/* directionDown= */ true, /* skipSubChannel= */ false);
});
- assertWithMessage("Exception for stepping when HAL is in invalid state")
+ mExpect.withMessage("Exception for stepping when HAL is in invalid state")
.that(thrown).hasMessageThat().contains(Result.toString(Result.INVALID_STATE));
}
@@ -533,7 +564,7 @@
mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false);
});
- assertWithMessage("Internal error HAL exception when seeking")
+ mExpect.withMessage("Internal error HAL exception when seeking")
.that(thrown).hasMessageThat().contains(Result.toString(Result.INTERNAL_ERROR));
}
@@ -566,7 +597,7 @@
mTunerSessions[0].cancel();
});
- assertWithMessage("Exception for canceling when HAL throws remote exception")
+ mExpect.withMessage("Exception for canceling when HAL throws remote exception")
.that(thrown).hasMessageThat().contains(exceptionMessage);
}
@@ -579,7 +610,7 @@
mTunerSessions[0].getImage(imageId);
});
- assertWithMessage("Get image exception")
+ mExpect.withMessage("Get image exception")
.that(thrown).hasMessageThat().contains("Image ID is missing");
}
@@ -590,7 +621,7 @@
Bitmap imageTest = mTunerSessions[0].getImage(imageId);
- assertWithMessage("Null image").that(imageTest).isEqualTo(null);
+ mExpect.withMessage("Null image").that(imageTest).isEqualTo(null);
}
@Test
@@ -603,7 +634,7 @@
mTunerSessions[0].getImage(/* id= */ 1);
});
- assertWithMessage("Exception for getting image when HAL throws remote exception")
+ mExpect.withMessage("Exception for getting image when HAL throws remote exception")
.that(thrown).hasMessageThat().contains(exceptionMessage);
}
@@ -649,7 +680,7 @@
mTunerSessions[0].startProgramListUpdates(/* filter= */ null);
});
- assertWithMessage("Unknown error HAL exception when updating program list")
+ mExpect.withMessage("Unknown error HAL exception when updating program list")
.that(thrown).hasMessageThat().contains(Result.toString(Result.UNKNOWN_ERROR));
}
@@ -686,7 +717,7 @@
boolean isSupported = mTunerSessions[0].isConfigFlagSupported(flag);
verify(mHalTunerSessionMock).isConfigFlagSet(eq(flag), any());
- assertWithMessage("Config flag %s is supported", flag).that(isSupported).isFalse();
+ mExpect.withMessage("Config flag %s is supported", flag).that(isSupported).isFalse();
}
@Test
@@ -697,7 +728,7 @@
boolean isSupported = mTunerSessions[0].isConfigFlagSupported(flag);
verify(mHalTunerSessionMock).isConfigFlagSet(eq(flag), any());
- assertWithMessage("Config flag %s is supported", flag).that(isSupported).isTrue();
+ mExpect.withMessage("Config flag %s is supported", flag).that(isSupported).isTrue();
}
@Test
@@ -709,7 +740,7 @@
mTunerSessions[0].setConfigFlag(flag, /* value= */ true);
});
- assertWithMessage("Exception for setting unsupported flag %s", flag)
+ mExpect.withMessage("Exception for setting unsupported flag %s", flag)
.that(thrown).hasMessageThat().contains("setConfigFlag: NOT_SUPPORTED");
}
@@ -755,7 +786,7 @@
mTunerSessions[0].isConfigFlagSet(flag);
});
- assertWithMessage("Exception for checking if unsupported flag %s is set", flag)
+ mExpect.withMessage("Exception for checking if unsupported flag %s is set", flag)
.that(thrown).hasMessageThat().contains("isConfigFlagSet: NOT_SUPPORTED");
}
@@ -768,7 +799,7 @@
boolean isSet = mTunerSessions[0].isConfigFlagSet(flag);
- assertWithMessage("Config flag %s is set", flag)
+ mExpect.withMessage("Config flag %s is set", flag)
.that(isSet).isEqualTo(expectedConfigFlagValue);
}
@@ -782,7 +813,7 @@
mTunerSessions[0].isConfigFlagSet(flag);
});
- assertWithMessage("Exception for checking config flag when HAL throws remote exception")
+ mExpect.withMessage("Exception for checking config flag when HAL throws remote exception")
.that(thrown).hasMessageThat().contains("Failed to check flag");
}
@@ -822,7 +853,7 @@
mTunerSessions[0].setParameters(parametersSet);
});
- assertWithMessage("Exception for setting parameters when HAL throws remote exception")
+ mExpect.withMessage("Exception for setting parameters when HAL throws remote exception")
.that(thrown).hasMessageThat().contains(exceptionMessage);
}
@@ -848,7 +879,7 @@
mTunerSessions[0].getParameters(parameterKeys);
});
- assertWithMessage("Exception for getting parameters when HAL throws remote exception")
+ mExpect.withMessage("Exception for getting parameters when HAL throws remote exception")
.that(thrown).hasMessageThat().contains(exceptionMessage);
}
@@ -894,6 +925,36 @@
}
}
+ @Test
+ public void openSession_withNonNullAntennaState() throws Exception {
+ boolean antennaConnected = false;
+ android.hardware.radio.ITunerCallback callback =
+ mock(android.hardware.radio.ITunerCallback.class);
+ openAidlClients(/* numClients= */ 1);
+ mHalTunerCallback.onAntennaStateChange(antennaConnected);
+ verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onAntennaState(antennaConnected);
+
+ mRadioModule.openSession(callback);
+
+ verify(callback, CALLBACK_TIMEOUT).onAntennaState(antennaConnected);
+ }
+
+ @Test
+ public void openSession_withNonNullCurrentProgramInfo() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
+ RadioManager.ProgramInfo tuneInfo = TestUtils.makeProgramInfo(initialSel,
+ SIGNAL_QUALITY);
+ mTunerSessions[0].tune(initialSel);
+ verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo);
+ android.hardware.radio.ITunerCallback callback =
+ mock(android.hardware.radio.ITunerCallback.class);
+
+ mRadioModule.openSession(callback);
+
+ verify(callback, CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo);
+ }
+
private void openAidlClients(int numClients) throws Exception {
mAidlTunerCallbackMocks = new android.hardware.radio.ITunerCallback[numClients];
mTunerSessions = new TunerSession[numClients];
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 5d52da1..06cb0ee 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -1224,11 +1224,8 @@
// Infrequent update
Thread.sleep(delay);
- // Even though this is not a small View, step 3 is triggered by this flag, which
- // brings intermittent to LOW
- int intermittentExpected = toolkitFrameRateBySizeReadOnly()
- ? FRAME_RATE_CATEGORY_LOW
- : FRAME_RATE_CATEGORY_NORMAL;
+ // The expected category is normal for intermittent.
+ int intermittentExpected = FRAME_RATE_CATEGORY_NORMAL;
sInstrumentation.runOnMainSync(() -> {
mView.invalidate();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 0119289..0fd21f3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -30,6 +30,7 @@
import android.content.Context;
import android.content.res.Configuration;
import android.database.ContentObserver;
+import android.graphics.Rect;
import android.hardware.input.InputManager;
import android.net.Uri;
import android.os.Bundle;
@@ -47,6 +48,7 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.RemoteAnimationTarget;
+import android.view.WindowManager;
import android.window.BackAnimationAdapter;
import android.window.BackEvent;
import android.window.BackMotionEvent;
@@ -119,6 +121,9 @@
private final ShellCommandHandler mShellCommandHandler;
private final ShellExecutor mShellExecutor;
private final Handler mBgHandler;
+ private final WindowManager mWindowManager;
+ @VisibleForTesting
+ final Rect mTouchableArea = new Rect();
/**
* Tracks the current user back gesture.
@@ -222,6 +227,8 @@
mShellBackAnimationRegistry = shellBackAnimationRegistry;
mLatencyTracker = LatencyTracker.getInstance(mContext);
mShellCommandHandler = shellCommandHandler;
+ mWindowManager = context.getSystemService(WindowManager.class);
+ updateTouchableArea();
}
private void onInit() {
@@ -283,6 +290,11 @@
@Override
public void onConfigurationChanged(Configuration newConfig) {
mShellBackAnimationRegistry.onConfigurationChanged(newConfig);
+ updateTouchableArea();
+ }
+
+ private void updateTouchableArea() {
+ mTouchableArea.set(mWindowManager.getCurrentWindowMetrics().getBounds());
}
@Override
@@ -416,11 +428,18 @@
if (!shouldDispatchToAnimator && mActiveCallback != null) {
mCurrentTracker.updateStartLocation();
tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent(null));
+ if (mBackNavigationInfo != null && !isAppProgressGenerationAllowed()) {
+ tryPilferPointers();
+ }
} else if (shouldDispatchToAnimator) {
tryPilferPointers();
}
}
+ private boolean isAppProgressGenerationAllowed() {
+ return mBackNavigationInfo.getTouchableRegion().equals(mTouchableArea);
+ }
+
/**
* Called when a new motion event needs to be transferred to this
* {@link BackAnimationController}
@@ -536,6 +555,9 @@
// App is handling back animation. Cancel system animation latency tracking.
cancelLatencyTracking();
tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null));
+ if (!isAppProgressGenerationAllowed()) {
+ tryPilferPointers();
+ }
}
}
@@ -642,7 +664,8 @@
private void dispatchOnBackProgressed(IOnBackInvokedCallback callback,
BackMotionEvent backEvent) {
- if (callback == null || !shouldDispatchToAnimator()) {
+ if (callback == null || (!shouldDispatchToAnimator() && mBackNavigationInfo != null
+ && isAppProgressGenerationAllowed())) {
return;
}
try {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl
index b5f25433f..e779879 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl
@@ -53,9 +53,11 @@
* @param destinationBounds the destination bounds the PiP window lands into
* @param overlay an optional overlay to fade out after entering PiP
* @param appBounds the bounds used to set the buffer size of the optional content overlay
+ * @param sourceRectHint the bounds to show in the transition to PiP
*/
oneway void stopSwipePipToHome(int taskId, in ComponentName componentName,
- in Rect destinationBounds, in SurfaceControl overlay, in Rect appBounds) = 2;
+ in Rect destinationBounds, in SurfaceControl overlay, in Rect appBounds,
+ in Rect sourceRectHint) = 2;
/**
* Notifies the swiping Activity to PiP onto home transition is aborted
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
index 579a794..a09720d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
@@ -22,6 +22,7 @@
import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
+import android.graphics.Rect
import android.os.RemoteException
import android.os.SystemProperties
import android.util.DisplayMetrics
@@ -138,6 +139,30 @@
}
}
+
+ /**
+ * Returns a fake source rect hint for animation purposes when app-provided one is invalid.
+ * Resulting adjusted source rect hint lets the app icon in the content overlay to stay visible.
+ */
+ @JvmStatic
+ fun getEnterPipWithOverlaySrcRectHint(appBounds: Rect, aspectRatio: Float): Rect {
+ val appBoundsAspRatio = appBounds.width().toFloat() / appBounds.height()
+ val width: Int
+ val height: Int
+ var left = 0
+ var top = 0
+ if (appBoundsAspRatio < aspectRatio) {
+ width = appBounds.width()
+ height = Math.round(width / aspectRatio)
+ top = (appBounds.height() - height) / 2
+ } else {
+ height = appBounds.height()
+ width = Math.round(height * aspectRatio)
+ left = (appBounds.width() - width) / 2
+ }
+ return Rect(left, top, left + width, top + height)
+ }
+
private var isPip2ExperimentEnabled: Boolean? = null
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java
index 835f1af..07082a5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java
@@ -53,7 +53,7 @@
private final ShellExecutor mMainExecutor;
- private boolean mIsActivityLetterboxed;
+ private boolean mIsLetterboxDoubleTapEnabled;
private int mLetterboxVerticalPosition;
@@ -91,7 +91,7 @@
Function<Integer, Integer> disappearTimeSupplier) {
super(context, taskInfo, syncQueue, taskListener, displayLayout);
final AppCompatTaskInfo appCompatTaskInfo = taskInfo.appCompatTaskInfo;
- mIsActivityLetterboxed = appCompatTaskInfo.isLetterboxDoubleTapEnabled;
+ mIsLetterboxDoubleTapEnabled = appCompatTaskInfo.isLetterboxDoubleTapEnabled;
mLetterboxVerticalPosition = appCompatTaskInfo.topActivityLetterboxVerticalPosition;
mLetterboxHorizontalPosition = appCompatTaskInfo.topActivityLetterboxHorizontalPosition;
mTopActivityLetterboxWidth = appCompatTaskInfo.topActivityLetterboxWidth;
@@ -119,7 +119,7 @@
@Override
protected boolean eligibleToShowLayout() {
- return mIsActivityLetterboxed
+ return mIsLetterboxDoubleTapEnabled
&& (mLetterboxVerticalPosition != -1 || mLetterboxHorizontalPosition != -1);
}
@@ -142,13 +142,13 @@
@Override
public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener,
boolean canShow) {
- final boolean prevIsActivityLetterboxed = mIsActivityLetterboxed;
+ final boolean prevIsLetterboxDoubleTapEnabled = mIsLetterboxDoubleTapEnabled;
final int prevLetterboxVerticalPosition = mLetterboxVerticalPosition;
final int prevLetterboxHorizontalPosition = mLetterboxHorizontalPosition;
final int prevTopActivityLetterboxWidth = mTopActivityLetterboxWidth;
final int prevTopActivityLetterboxHeight = mTopActivityLetterboxHeight;
final AppCompatTaskInfo appCompatTaskInfo = taskInfo.appCompatTaskInfo;
- mIsActivityLetterboxed = appCompatTaskInfo.isLetterboxDoubleTapEnabled;
+ mIsLetterboxDoubleTapEnabled = appCompatTaskInfo.isLetterboxDoubleTapEnabled;
mLetterboxVerticalPosition = appCompatTaskInfo.topActivityLetterboxVerticalPosition;
mLetterboxHorizontalPosition = appCompatTaskInfo.topActivityLetterboxHorizontalPosition;
mTopActivityLetterboxWidth = appCompatTaskInfo.topActivityLetterboxWidth;
@@ -162,7 +162,7 @@
mHasLetterboxSizeChanged = prevTopActivityLetterboxWidth != mTopActivityLetterboxWidth
|| prevTopActivityLetterboxHeight != mTopActivityLetterboxHeight;
- if (mHasUserDoubleTapped || prevIsActivityLetterboxed != mIsActivityLetterboxed
+ if (mHasUserDoubleTapped || prevIsLetterboxDoubleTapEnabled != mIsLetterboxDoubleTapEnabled
|| prevLetterboxVerticalPosition != mLetterboxVerticalPosition
|| prevLetterboxHorizontalPosition != mLetterboxHorizontalPosition
|| prevTopActivityLetterboxWidth != mTopActivityLetterboxWidth
@@ -249,7 +249,7 @@
&& (mLetterboxVerticalPosition == REACHABILITY_LEFT_OR_UP_POSITION
|| mLetterboxVerticalPosition == REACHABILITY_RIGHT_OR_BOTTOM_POSITION));
- if (mIsActivityLetterboxed && (eligibleForDisplayHorizontalEducation
+ if (mIsLetterboxDoubleTapEnabled && (eligibleForDisplayHorizontalEducation
|| eligibleForDisplayVerticalEducation)) {
int availableWidth = getTaskBounds().width() - mTopActivityLetterboxWidth;
int availableHeight = getTaskBounds().height() - mTopActivityLetterboxHeight;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
index 109868d..9192e6e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
@@ -187,7 +187,10 @@
KEYBOARD_SHORTCUT_ENTER(
FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__KEYBOARD_SHORTCUT_ENTER
),
- SCREEN_ON(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__SCREEN_ON)
+ SCREEN_ON(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__SCREEN_ON),
+ APP_FROM_OVERVIEW(
+ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__APP_FROM_OVERVIEW
+ ),
}
/**
@@ -204,7 +207,7 @@
FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__KEYBOARD_SHORTCUT_EXIT
),
RETURN_HOME_OR_OVERVIEW(
- FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME
+ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__RETURN_HOME_OR_OVERVIEW
),
TASK_FINISHED(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__TASK_FINISHED),
SCREEN_OFF(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__SCREEN_OFF)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index 075e3ae..cee2d92 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
@@ -314,8 +314,7 @@
WindowManager.TRANSIT_WAKE -> EnterReason.SCREEN_ON
Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP -> EnterReason.APP_HANDLE_DRAG
TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON -> EnterReason.APP_HANDLE_MENU_BUTTON
- // TODO(b/344822506): Create and update EnterReason to APP_FROM_OVERVIEW
- TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW -> EnterReason.UNKNOWN_ENTER
+ TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW -> EnterReason.APP_FROM_OVERVIEW
TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT -> EnterReason.KEYBOARD_SHORTCUT_ENTER
WindowManager.TRANSIT_OPEN -> EnterReason.APP_FREEFORM_INTENT
else -> EnterReason.UNKNOWN_ENTER
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 57c0732..0a3c15b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -40,6 +40,7 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.transition.Transitions;
@@ -619,19 +620,8 @@
// This is done for entering case only.
if (isInPipDirection(direction)) {
final float aspectRatio = endValue.width() / (float) endValue.height();
- if ((startValue.width() / (float) startValue.height()) > aspectRatio) {
- // use the full height.
- adjustedSourceRectHint.set(0, 0,
- (int) (startValue.height() * aspectRatio), startValue.height());
- adjustedSourceRectHint.offset(
- (startValue.width() - adjustedSourceRectHint.width()) / 2, 0);
- } else {
- // use the full width.
- adjustedSourceRectHint.set(0, 0,
- startValue.width(), (int) (startValue.width() / aspectRatio));
- adjustedSourceRectHint.offset(
- 0, (startValue.height() - adjustedSourceRectHint.height()) / 2);
- }
+ adjustedSourceRectHint.set(PipUtils.getEnterPipWithOverlaySrcRectHint(
+ startValue, aspectRatio));
}
} else {
adjustedSourceRectHint.set(sourceRectHint);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 04dd0ef..e4420d7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -373,6 +373,10 @@
@NonNull
final Rect mAppBounds = new Rect();
+ /** The source rect hint from stopSwipePipToHome(). */
+ @Nullable
+ private Rect mSwipeSourceRectHint;
+
public PipTaskOrganizer(Context context,
@NonNull SyncTransactionQueue syncTransactionQueue,
@NonNull PipTransitionState pipTransitionState,
@@ -504,7 +508,7 @@
* Expect {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)} afterwards.
*/
public void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds,
- SurfaceControl overlay, Rect appBounds) {
+ SurfaceControl overlay, Rect appBounds, Rect sourceRectHint) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"stopSwipePipToHome: %s, stat=%s", componentName, mPipTransitionState);
// do nothing if there is no startSwipePipToHome being called before
@@ -513,6 +517,7 @@
}
mPipBoundsState.setBounds(destinationBounds);
setContentOverlay(overlay, appBounds);
+ mSwipeSourceRectHint = sourceRectHint;
if (ENABLE_SHELL_TRANSITIONS && overlay != null) {
// With Shell transition, the overlay was attached to the remote transition leash, which
// will be removed when the current transition is finished, so we need to reparent it
@@ -529,6 +534,20 @@
}
}
+ /**
+ * Returns non-null Rect if the pip is entering from swipe-to-home with a specified source hint.
+ * This also consumes the rect hint.
+ */
+ @Nullable
+ Rect takeSwipeSourceRectHint() {
+ final Rect sourceRectHint = mSwipeSourceRectHint;
+ if (sourceRectHint == null || sourceRectHint.isEmpty()) {
+ return null;
+ }
+ mSwipeSourceRectHint = null;
+ return mPipTransitionState.getInSwipePipToHomeTransition() ? sourceRectHint : null;
+ }
+
private void mayRemoveContentOverlay(SurfaceControl overlay) {
final WeakReference<SurfaceControl> overlayRef = new WeakReference<>(overlay);
final long timeoutDuration = (mEnterAnimationDuration
@@ -980,7 +999,6 @@
private void onEndOfSwipePipToHomeTransition() {
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- mPipTransitionController.setEnterAnimationType(ANIM_TYPE_BOUNDS);
return;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 5ee6f6b..3cae72d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -1004,8 +1004,11 @@
final Rect currentBounds = pipChange.getStartAbsBounds();
int rotationDelta = deltaRotation(startRotation, endRotation);
- Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
- taskInfo.pictureInPictureParams, currentBounds, destinationBounds);
+ Rect sourceHintRect = mPipOrganizer.takeSwipeSourceRectHint();
+ if (sourceHintRect == null) {
+ sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
+ taskInfo.pictureInPictureParams, currentBounds, destinationBounds);
+ }
if (rotationDelta != Surface.ROTATION_0
&& endRotation != mPipDisplayLayoutState.getRotation()) {
// Computes the destination bounds in new rotation.
@@ -1080,6 +1083,8 @@
mSurfaceTransactionHelper
.crop(finishTransaction, leash, destinationBounds)
.round(finishTransaction, leash, true /* applyCornerRadius */);
+ // Always reset to bounds animation type afterwards.
+ setEnterAnimationType(ANIM_TYPE_BOUNDS);
} else {
throw new RuntimeException("Unrecognized animation type: " + enterAnimationType);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index de105c0..8c4bf76 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -1001,9 +1001,9 @@
}
private void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds,
- SurfaceControl overlay, Rect appBounds) {
+ SurfaceControl overlay, Rect appBounds, Rect sourceRectHint) {
mPipTaskOrganizer.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay,
- appBounds);
+ appBounds, sourceRectHint);
}
private void abortSwipePipToHome(int taskId, ComponentName componentName) {
@@ -1291,13 +1291,15 @@
@Override
public void stopSwipePipToHome(int taskId, ComponentName componentName,
- Rect destinationBounds, SurfaceControl overlay, Rect appBounds) {
+ Rect destinationBounds, SurfaceControl overlay, Rect appBounds,
+ Rect sourceRectHint) {
if (overlay != null) {
overlay.setUnreleasedWarningCallSite("PipController.stopSwipePipToHome");
}
executeRemoteCallWithTaskPermission(mController, "stopSwipePipToHome",
(controller) -> controller.stopSwipePipToHome(
- taskId, componentName, destinationBounds, overlay, appBounds));
+ taskId, componentName, destinationBounds, overlay, appBounds,
+ sourceRectHint));
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java
index 3e298e5..895c2ae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java
@@ -19,11 +19,13 @@
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
+import android.content.Context;
import android.view.SurfaceControl;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.wm.shell.R;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import java.lang.annotation.Retention;
@@ -45,6 +47,7 @@
public static final int FADE_IN = 0;
public static final int FADE_OUT = 1;
+ private final int mEnterAnimationDuration;
private final SurfaceControl mLeash;
private final SurfaceControl.Transaction mStartTransaction;
@@ -55,7 +58,8 @@
private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
- public PipAlphaAnimator(SurfaceControl leash,
+ public PipAlphaAnimator(Context context,
+ SurfaceControl leash,
SurfaceControl.Transaction tx,
@Fade int direction) {
mLeash = leash;
@@ -67,6 +71,9 @@
}
mSurfaceControlTransactionFactory =
new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
+ mEnterAnimationDuration = context.getResources()
+ .getInteger(R.integer.config_pipEnterAnimationDuration);
+ setDuration(mEnterAnimationDuration);
addListener(this);
addUpdateListener(this);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index 1846720..fc0d36d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -285,7 +285,8 @@
}
private void onSwipePipToHomeAnimationStart(int taskId, ComponentName componentName,
- Rect destinationBounds, SurfaceControl overlay, Rect appBounds) {
+ Rect destinationBounds, SurfaceControl overlay, Rect appBounds,
+ Rect sourceRectHint) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"onSwipePipToHomeAnimationStart: %s", componentName);
Bundle extra = new Bundle();
@@ -409,13 +410,15 @@
@Override
public void stopSwipePipToHome(int taskId, ComponentName componentName,
- Rect destinationBounds, SurfaceControl overlay, Rect appBounds) {
+ Rect destinationBounds, SurfaceControl overlay, Rect appBounds,
+ Rect sourceRectHint) {
if (overlay != null) {
overlay.setUnreleasedWarningCallSite("PipController.stopSwipePipToHome");
}
executeRemoteCallWithTaskPermission(mController, "stopSwipePipToHome",
(controller) -> controller.onSwipePipToHomeAnimationStart(
- taskId, componentName, destinationBounds, overlay, appBounds));
+ taskId, componentName, destinationBounds, overlay, appBounds,
+ sourceRectHint));
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 3e215d9..0b2db6c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -293,37 +293,32 @@
return false;
}
+ SurfaceControl overlayLeash = mPipTransitionState.getSwipePipToHomeOverlay();
PictureInPictureParams params = pipChange.getTaskInfo().pictureInPictureParams;
- Rect srcRectHint = params.getSourceRectHint();
- Rect startBounds = pipChange.getStartAbsBounds();
+
+ Rect appBounds = mPipTransitionState.getSwipePipToHomeAppBounds();
Rect destinationBounds = pipChange.getEndAbsBounds();
+ float aspectRatio = pipChange.getTaskInfo().pictureInPictureParams.getAspectRatioFloat();
+
+ // We fake the source rect hint when the one prvided by the app is invalid for
+ // the animation with an app icon overlay.
+ Rect animationSrcRectHint = overlayLeash == null ? params.getSourceRectHint()
+ : PipUtils.getEnterPipWithOverlaySrcRectHint(appBounds, aspectRatio);
+
WindowContainerTransaction finishWct = new WindowContainerTransaction();
SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
- if (PipBoundsAlgorithm.isSourceRectHintValidForEnterPip(srcRectHint, destinationBounds)) {
- final float scale = (float) destinationBounds.width() / srcRectHint.width();
- startTransaction.setWindowCrop(pipLeash, srcRectHint);
- startTransaction.setPosition(pipLeash,
- destinationBounds.left - srcRectHint.left * scale,
- destinationBounds.top - srcRectHint.top * scale);
+ final float scale = (float) destinationBounds.width() / animationSrcRectHint.width();
+ startTransaction.setWindowCrop(pipLeash, animationSrcRectHint);
+ startTransaction.setPosition(pipLeash,
+ destinationBounds.left - animationSrcRectHint.left * scale,
+ destinationBounds.top - animationSrcRectHint.top * scale);
+ startTransaction.setScale(pipLeash, scale, scale);
- // Reset the scale in case we are in the multi-activity case.
- // TO_FRONT transition already scales down the task in single-activity case, but
- // in multi-activity case, reparenting yields new reset scales coming from pinned task.
- startTransaction.setScale(pipLeash, scale, scale);
- } else {
- final float scaleX = (float) destinationBounds.width() / startBounds.width();
- final float scaleY = (float) destinationBounds.height() / startBounds.height();
+ if (overlayLeash != null) {
final int overlaySize = PipContentOverlay.PipAppIconOverlay.getOverlaySize(
mPipTransitionState.getSwipePipToHomeAppBounds(), destinationBounds);
- SurfaceControl overlayLeash = mPipTransitionState.getSwipePipToHomeOverlay();
-
- startTransaction.setPosition(pipLeash, destinationBounds.left, destinationBounds.top)
- .setScale(pipLeash, scaleX, scaleY)
- .setWindowCrop(pipLeash, startBounds)
- .reparent(overlayLeash, pipLeash)
- .setLayer(overlayLeash, Integer.MAX_VALUE);
// Overlay needs to be adjusted once a new draw comes in resetting surface transform.
tx.setScale(overlayLeash, 1f, 1f);
@@ -390,15 +385,23 @@
if (pipChange == null) {
return false;
}
- // cache the PiP task token and leash
- WindowContainerToken pipTaskToken = pipChange.getContainer();
- Preconditions.checkNotNull(mPipLeash, "Leash is null for alpha transition.");
- // start transition with 0 alpha
- startTransaction.setAlpha(mPipLeash, 0f);
- PipAlphaAnimator animator = new PipAlphaAnimator(mPipLeash,
- startTransaction, PipAlphaAnimator.FADE_IN);
- animator.setAnimationEndCallback(() -> finishCallback.onTransitionFinished(null));
+ Rect destinationBounds = pipChange.getEndAbsBounds();
+ SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
+ Preconditions.checkNotNull(pipLeash, "Leash is null for alpha transition.");
+
+ // Start transition with 0 alpha at the entry bounds.
+ startTransaction.setPosition(pipLeash, destinationBounds.left, destinationBounds.top)
+ .setWindowCrop(pipLeash, destinationBounds.width(), destinationBounds.height())
+ .setAlpha(pipLeash, 0f);
+
+ PipAlphaAnimator animator = new PipAlphaAnimator(mContext, pipLeash, startTransaction,
+ PipAlphaAnimator.FADE_IN);
+ animator.setAnimationEndCallback(() -> {
+ finishCallback.onTransitionFinished(null);
+ // This should update the pip transition state accordingly after we stop playing.
+ onClientDrawAtTransitionEnd();
+ });
animator.start();
return true;
@@ -480,10 +483,10 @@
private boolean isLegacyEnter(@NonNull TransitionInfo info) {
TransitionInfo.Change pipChange = getPipChange(info);
- // If the only change in the changes list is a TO_FRONT mode PiP task,
+ // If the only change in the changes list is a opening type PiP task,
// then this is legacy-enter PiP.
- return pipChange != null && pipChange.getMode() == TRANSIT_TO_FRONT
- && info.getChanges().size() == 1;
+ return pipChange != null && info.getChanges().size() == 1
+ && (pipChange.getMode() == TRANSIT_TO_FRONT || pipChange.getMode() == TRANSIT_OPEN);
}
private boolean isRemovePipTransition(@NonNull TransitionInfo info) {
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 a4ade1b..3dcdc0b 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
@@ -233,32 +233,7 @@
}
if (oldRootView != mResult.mRootView) {
- if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) {
- mWindowDecorViewHolder = new AppHandleViewHolder(
- mResult.mRootView,
- mOnCaptionTouchListener,
- mOnCaptionButtonClickListener
- );
- } else if (mRelayoutParams.mLayoutResId
- == R.layout.desktop_mode_app_header) {
- loadAppInfoIfNeeded();
- mWindowDecorViewHolder = new AppHeaderViewHolder(
- mResult.mRootView,
- mOnCaptionTouchListener,
- mOnCaptionButtonClickListener,
- mOnCaptionLongClickListener,
- mOnCaptionGenericMotionListener,
- mAppName,
- mAppIconBitmap,
- () -> {
- if (!isMaximizeMenuActive()) {
- createMaximizeMenu();
- }
- return Unit.INSTANCE;
- });
- } else {
- throw new IllegalArgumentException("Unexpected layout resource id");
- }
+ mWindowDecorViewHolder = createViewHolder();
}
Trace.beginSection("DesktopModeWindowDecoration#relayout-binding");
mWindowDecorViewHolder.bindData(mTaskInfo);
@@ -269,16 +244,18 @@
closeMaximizeMenu();
}
- final boolean isFreeform =
- taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM;
- final boolean isDragResizeable = isFreeform && taskInfo.isResizeable;
- if (!isDragResizeable) {
+ updateDragResizeListener(oldDecorationSurface);
+ updateMaximizeMenu(startT);
+ Trace.endSection(); // DesktopModeWindowDecoration#relayout
+ }
+
+ private void updateDragResizeListener(SurfaceControl oldDecorationSurface) {
+ if (!isDragResizable(mTaskInfo)) {
if (!mTaskInfo.positionInParent.equals(mPositionInParent)) {
// We still want to track caption bar's exclusion region on a non-resizeable task.
updateExclusionRegion();
}
closeDragResizeListener();
- Trace.endSection(); // DesktopModeWindowDecoration#relayout
return;
}
@@ -312,15 +289,51 @@
|| !mTaskInfo.positionInParent.equals(mPositionInParent)) {
updateExclusionRegion();
}
+ }
- if (isMaximizeMenuActive()) {
- if (!mTaskInfo.isVisible()) {
- closeMaximizeMenu();
- } else {
- mMaximizeMenu.positionMenu(calculateMaximizeMenuPosition(), startT);
- }
+ private static boolean isDragResizable(ActivityManager.RunningTaskInfo taskInfo) {
+ final boolean isFreeform =
+ taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM;
+ return isFreeform && taskInfo.isResizeable;
+ }
+
+ private void updateMaximizeMenu(SurfaceControl.Transaction startT) {
+ if (!isDragResizable(mTaskInfo) || !isMaximizeMenuActive()) {
+ return;
}
- Trace.endSection(); // DesktopModeWindowDecoration#relayout
+ if (!mTaskInfo.isVisible()) {
+ closeMaximizeMenu();
+ } else {
+ mMaximizeMenu.positionMenu(calculateMaximizeMenuPosition(), startT);
+ }
+ }
+
+ private WindowDecorationViewHolder createViewHolder() {
+ if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) {
+ return new AppHandleViewHolder(
+ mResult.mRootView,
+ mOnCaptionTouchListener,
+ mOnCaptionButtonClickListener
+ );
+ } else if (mRelayoutParams.mLayoutResId
+ == R.layout.desktop_mode_app_header) {
+ loadAppInfoIfNeeded();
+ return new AppHeaderViewHolder(
+ mResult.mRootView,
+ mOnCaptionTouchListener,
+ mOnCaptionButtonClickListener,
+ mOnCaptionLongClickListener,
+ mOnCaptionGenericMotionListener,
+ mAppName,
+ mAppIconBitmap,
+ () -> {
+ if (!isMaximizeMenuActive()) {
+ createMaximizeMenu();
+ }
+ return Unit.INSTANCE;
+ });
+ }
+ throw new IllegalArgumentException("Unexpected layout resource id");
}
@VisibleForTesting
@@ -818,12 +831,12 @@
// We want handle to remain pressed if the pointer moves outside of it during a drag.
handle.setPressed((inHandle && action == ACTION_DOWN)
|| (handle.isPressed() && action != ACTION_UP && action != ACTION_CANCEL));
- if (isHandleMenuActive() && !isMenuAboveStatusBar()) {
+ if (isHandleMenuActive() && !isHandleMenuAboveStatusBar()) {
mHandleMenu.checkMotionEvent(ev);
}
}
- private boolean isMenuAboveStatusBar() {
+ private boolean isHandleMenuAboveStatusBar() {
return Flags.enableAdditionalWindowsAboveStatusBar() && !mTaskInfo.isFreeform();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
index bfc4e0d..df0836c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
@@ -24,6 +24,7 @@
import static android.view.MotionEvent.ACTION_UP;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -35,6 +36,7 @@
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Color;
+import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.view.MotionEvent;
@@ -70,8 +72,14 @@
private final DesktopModeWindowDecoration mParentDecor;
@VisibleForTesting
AdditionalViewContainer mHandleMenuViewContainer;
+ // Position of the handle menu used for laying out the handle view.
@VisibleForTesting
final PointF mHandleMenuPosition = new PointF();
+ // With the introduction of {@link AdditionalSystemViewContainer}, {@link mHandleMenuPosition}
+ // may be in a different coordinate space than the input coordinates. Therefore, we still care
+ // about the menu's coordinates relative to the display as a whole, so we need to maintain
+ // those as well.
+ final Point mGlobalMenuPosition = new Point();
private final boolean mShouldShowWindowingPill;
private final Bitmap mAppIconBitmap;
private final CharSequence mAppName;
@@ -244,39 +252,23 @@
private void updateHandleMenuPillPositions() {
int menuX;
final int menuY;
+ final Rect taskBounds = mTaskInfo.getConfiguration().windowConfiguration.getBounds();
+ updateGlobalMenuPosition(taskBounds);
if (mLayoutResId == R.layout.desktop_mode_app_header) {
// Align the handle menu to the left side of the caption.
menuX = mMarginMenuStart;
menuY = mMarginMenuTop;
} else {
- final int handleWidth = loadDimensionPixelSize(mContext.getResources(),
- R.dimen.desktop_mode_fullscreen_decor_caption_width);
- final int handleOffset = (mMenuWidth / 2) - (handleWidth / 2);
- final int captionX = mParentDecor.getCaptionX();
- // TODO(b/343561161): This needs to be calculated differently if the task is in
- // top/bottom split.
if (Flags.enableAdditionalWindowsAboveStatusBar()) {
- final Rect leftOrTopStageBounds = new Rect();
- if (mSplitScreenController.getSplitPosition(mTaskInfo.taskId)
- == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
- mSplitScreenController.getStageBounds(leftOrTopStageBounds, new Rect());
- }
// In a focused decor, we use global coordinates for handle menu. Therefore we
// need to account for other factors like split stage and menu/handle width to
// center the menu.
final DisplayLayout layout = mDisplayController
.getDisplayLayout(mTaskInfo.displayId);
- menuX = captionX + handleOffset - (layout.width() / 2);
- if (mSplitScreenController.getSplitPosition(mTaskInfo.taskId)
- == SPLIT_POSITION_BOTTOM_OR_RIGHT && layout.isLandscape()) {
- // If this task in the right stage, we need to offset by left stage's width
- menuX += leftOrTopStageBounds.width();
- }
- menuY = mMarginMenuStart - ((layout.height() - mMenuHeight) / 2);
+ menuX = mGlobalMenuPosition.x + ((mMenuWidth - layout.width()) / 2);
+ menuY = mGlobalMenuPosition.y + ((mMenuHeight - layout.height()) / 2);
} else {
- final int captionWidth = mTaskInfo.getConfiguration()
- .windowConfiguration.getBounds().width();
- menuX = (captionWidth / 2) - (mMenuWidth / 2);
+ menuX = (taskBounds.width() / 2) - (mMenuWidth / 2);
menuY = mMarginMenuTop;
}
}
@@ -284,6 +276,36 @@
mHandleMenuPosition.set(menuX, menuY);
}
+ private void updateGlobalMenuPosition(Rect taskBounds) {
+ if (mTaskInfo.isFreeform()) {
+ mGlobalMenuPosition.set(taskBounds.left + mMarginMenuStart,
+ taskBounds.top + mMarginMenuTop);
+ } else if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+ mGlobalMenuPosition.set(
+ (taskBounds.width() / 2) - (mMenuWidth / 2) + mMarginMenuStart,
+ mMarginMenuTop
+ );
+ } else if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) {
+ final int splitPosition = mSplitScreenController.getSplitPosition(mTaskInfo.taskId);
+ final Rect leftOrTopStageBounds = new Rect();
+ final Rect rightOrBottomStageBounds = new Rect();
+ mSplitScreenController.getStageBounds(leftOrTopStageBounds,
+ rightOrBottomStageBounds);
+ // TODO(b/343561161): This needs to be calculated differently if the task is in
+ // top/bottom split.
+ if (splitPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
+ mGlobalMenuPosition.set(leftOrTopStageBounds.width()
+ + (rightOrBottomStageBounds.width() / 2)
+ - (mMenuWidth / 2) + mMarginMenuStart,
+ mMarginMenuTop);
+ } else if (splitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
+ mGlobalMenuPosition.set((leftOrTopStageBounds.width() / 2)
+ - (mMenuWidth / 2) + mMarginMenuStart,
+ mMarginMenuTop);
+ }
+ }
+ }
+
/**
* Update pill layout, in case task changes have caused positioning to change.
*/
@@ -302,6 +324,8 @@
* @param ev the MotionEvent to compare against.
*/
void checkMotionEvent(MotionEvent ev) {
+ // If the menu view is above status bar, we can let the views handle input directly.
+ if (isViewAboveStatusBar()) return;
final View handleMenu = mHandleMenuViewContainer.getView();
final HandleMenuImageButton collapse = handleMenu.findViewById(R.id.collapse_menu_button);
final PointF inputPoint = translateInputToLocalSpace(ev);
@@ -314,6 +338,11 @@
}
}
+ private boolean isViewAboveStatusBar() {
+ return Flags.enableAdditionalWindowsAboveStatusBar()
+ && !mTaskInfo.isFreeform();
+ }
+
// Translate the input point from display coordinates to the same space as the handle menu.
private PointF translateInputToLocalSpace(MotionEvent ev) {
return new PointF(ev.getX() - mHandleMenuPosition.x,
@@ -329,10 +358,33 @@
*/
boolean isValidMenuInput(PointF inputPoint) {
if (!viewsLaidOut()) return true;
- return pointInView(
- mHandleMenuViewContainer.getView(),
- inputPoint.x - mHandleMenuPosition.x,
- inputPoint.y - mHandleMenuPosition.y);
+ if (!isViewAboveStatusBar()) {
+ return pointInView(
+ mHandleMenuViewContainer.getView(),
+ inputPoint.x - mHandleMenuPosition.x,
+ inputPoint.y - mHandleMenuPosition.y);
+ } else {
+ // Handle menu exists in a different coordinate space when added to WindowManager.
+ // Therefore we must compare the provided input coordinates to global menu coordinates.
+ // This includes factoring for split stage as input coordinates are relative to split
+ // stage position, not relative to the display as a whole.
+ PointF inputRelativeToMenu = new PointF(
+ inputPoint.x - mGlobalMenuPosition.x,
+ inputPoint.y - mGlobalMenuPosition.y
+ );
+ if (mSplitScreenController.getSplitPosition(mTaskInfo.taskId)
+ == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
+ // TODO(b/343561161): This also needs to be calculated differently if
+ // the task is in top/bottom split.
+ Rect leftStageBounds = new Rect();
+ mSplitScreenController.getStageBounds(leftStageBounds, new Rect());
+ inputRelativeToMenu.x += leftStageBounds.width();
+ }
+ return pointInView(
+ mHandleMenuViewContainer.getView(),
+ inputRelativeToMenu.x,
+ inputRelativeToMenu.y);
+ }
}
private boolean pointInView(View v, float x, float y) {
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 a08f97c..b9532dd 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
@@ -213,13 +213,39 @@
return;
}
+ inflateIfNeeded(params, wct, rootView, oldLayoutResId, outResult);
+ if (outResult.mRootView == null) {
+ // Didn't manage to create a root view, early out.
+ return;
+ }
+ rootView = null; // Clear it just in case we use it accidentally
+
+ updateCaptionVisibility(outResult.mRootView, mTaskInfo.displayId);
+
+ final Rect taskBounds = mTaskInfo.getConfiguration().windowConfiguration.getBounds();
+ outResult.mWidth = taskBounds.width();
+ outResult.mHeight = taskBounds.height();
+ outResult.mRootView.setTaskFocusState(mTaskInfo.isFocused);
+ final Resources resources = mDecorWindowContext.getResources();
+ outResult.mCaptionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId);
+ outResult.mCaptionWidth = params.mCaptionWidthId != Resources.ID_NULL
+ ? loadDimensionPixelSize(resources, params.mCaptionWidthId) : taskBounds.width();
+ outResult.mCaptionX = (outResult.mWidth - outResult.mCaptionWidth) / 2;
+
+ updateDecorationContainerSurface(startT, outResult);
+ updateCaptionContainerSurface(startT, outResult);
+ updateCaptionInsets(params, wct, outResult, taskBounds);
+ updateTaskSurface(params, startT, finishT, outResult);
+ updateViewHost(params, startT, outResult);
+ }
+
+ private void inflateIfNeeded(RelayoutParams params, WindowContainerTransaction wct,
+ T rootView, int oldLayoutResId, RelayoutResult<T> outResult) {
if (rootView == null && params.mLayoutResId == 0) {
throw new IllegalArgumentException("layoutResId and rootView can't both be invalid.");
}
outResult.mRootView = rootView;
- rootView = null; // Clear it just in case we use it accidentally
-
final int oldDensityDpi = mWindowDecorConfig != null
? mWindowDecorConfig.densityDpi : DENSITY_DPI_UNDEFINED;
final int oldNightMode = mWindowDecorConfig != null
@@ -253,25 +279,17 @@
outResult.mRootView = (T) LayoutInflater.from(mDecorWindowContext)
.inflate(params.mLayoutResId, null);
}
+ }
- updateCaptionVisibility(outResult.mRootView, mTaskInfo.displayId);
-
- final Resources resources = mDecorWindowContext.getResources();
- final Configuration taskConfig = mTaskInfo.getConfiguration();
- final Rect taskBounds = taskConfig.windowConfiguration.getBounds();
- final boolean isFullscreen = taskConfig.windowConfiguration.getWindowingMode()
- == WINDOWING_MODE_FULLSCREEN;
- outResult.mWidth = taskBounds.width();
- outResult.mHeight = taskBounds.height();
-
- // DecorationContainerSurface
+ private void updateDecorationContainerSurface(
+ SurfaceControl.Transaction startT, RelayoutResult<T> outResult) {
if (mDecorationContainerSurface == null) {
final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
mDecorationContainerSurface = builder
.setName("Decor container of Task=" + mTaskInfo.taskId)
.setContainerLayer()
.setParent(mTaskSurface)
- .setCallsite("WindowDecoration.relayout_1")
+ .setCallsite("WindowDecoration.updateDecorationContainerSurface")
.build();
startT.setTrustedOverlay(mDecorationContainerSurface, true)
@@ -281,101 +299,101 @@
startT.setWindowCrop(mDecorationContainerSurface, outResult.mWidth, outResult.mHeight)
.show(mDecorationContainerSurface);
+ }
- // CaptionContainerSurface, CaptionWindowManager
+ private void updateCaptionContainerSurface(
+ SurfaceControl.Transaction startT, RelayoutResult<T> outResult) {
if (mCaptionContainerSurface == null) {
final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
mCaptionContainerSurface = builder
.setName("Caption container of Task=" + mTaskInfo.taskId)
.setContainerLayer()
.setParent(mDecorationContainerSurface)
- .setCallsite("WindowDecoration.relayout_2")
+ .setCallsite("WindowDecoration.updateCaptionContainerSurface")
.build();
}
- outResult.mCaptionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId);
- outResult.mCaptionWidth = params.mCaptionWidthId != Resources.ID_NULL
- ? loadDimensionPixelSize(resources, params.mCaptionWidthId) : taskBounds.width();
- outResult.mCaptionX = (outResult.mWidth - outResult.mCaptionWidth) / 2;
-
startT.setWindowCrop(mCaptionContainerSurface, outResult.mCaptionWidth,
outResult.mCaptionHeight)
.setPosition(mCaptionContainerSurface, outResult.mCaptionX, 0 /* y */)
.setLayer(mCaptionContainerSurface, CAPTION_LAYER_Z_ORDER)
.show(mCaptionContainerSurface);
+ }
- outResult.mRootView.setTaskFocusState(mTaskInfo.isFocused);
-
- // Caption insets
- if (mIsCaptionVisible) {
- // Caption inset is the full width of the task with the |captionHeight| and
- // positioned at the top of the task bounds, also in absolute coordinates.
- // So just reuse the task bounds and adjust the bottom coordinate.
- final Rect captionInsetsRect = new Rect(taskBounds);
- captionInsetsRect.bottom = captionInsetsRect.top + outResult.mCaptionHeight;
-
- // Caption bounding rectangles: these are optional, and are used to present finer
- // insets than traditional |Insets| to apps about where their content is occluded.
- // These are also in absolute coordinates.
- final Rect[] boundingRects;
- final int numOfElements = params.mOccludingCaptionElements.size();
- if (numOfElements == 0) {
- boundingRects = null;
- } else {
- // The customizable region can at most be equal to the caption bar.
- if (params.hasInputFeatureSpy()) {
- outResult.mCustomizableCaptionRegion.set(captionInsetsRect);
- }
- boundingRects = new Rect[numOfElements];
- for (int i = 0; i < numOfElements; i++) {
- final OccludingCaptionElement element =
- params.mOccludingCaptionElements.get(i);
- final int elementWidthPx =
- resources.getDimensionPixelSize(element.mWidthResId);
- boundingRects[i] =
- calculateBoundingRect(element, elementWidthPx, captionInsetsRect);
- // Subtract the regions used by the caption elements, the rest is
- // customizable.
- if (params.hasInputFeatureSpy()) {
- outResult.mCustomizableCaptionRegion.op(boundingRects[i],
- Region.Op.DIFFERENCE);
- }
- }
- }
-
- final WindowDecorationInsets newInsets = new WindowDecorationInsets(
- mTaskInfo.token, mOwner, captionInsetsRect, boundingRects);
- if (!newInsets.equals(mWindowDecorationInsets)) {
- // Add or update this caption as an insets source.
- mWindowDecorationInsets = newInsets;
- mWindowDecorationInsets.addOrUpdate(wct);
- }
- } else {
+ private void updateCaptionInsets(RelayoutParams params, WindowContainerTransaction wct,
+ RelayoutResult<T> outResult, Rect taskBounds) {
+ if (!mIsCaptionVisible) {
if (mWindowDecorationInsets != null) {
mWindowDecorationInsets.remove(wct);
mWindowDecorationInsets = null;
}
+ return;
}
+ // Caption inset is the full width of the task with the |captionHeight| and
+ // positioned at the top of the task bounds, also in absolute coordinates.
+ // So just reuse the task bounds and adjust the bottom coordinate.
+ final Rect captionInsetsRect = new Rect(taskBounds);
+ captionInsetsRect.bottom = captionInsetsRect.top + outResult.mCaptionHeight;
- // Task surface itself
- float shadowRadius;
- final Point taskPosition = mTaskInfo.positionInParent;
- if (isFullscreen) {
- // Shadow is not needed for fullscreen tasks
- shadowRadius = 0;
+ // Caption bounding rectangles: these are optional, and are used to present finer
+ // insets than traditional |Insets| to apps about where their content is occluded.
+ // These are also in absolute coordinates.
+ final Rect[] boundingRects;
+ final int numOfElements = params.mOccludingCaptionElements.size();
+ if (numOfElements == 0) {
+ boundingRects = null;
} else {
- shadowRadius = loadDimension(resources, params.mShadowRadiusId);
+ // The customizable region can at most be equal to the caption bar.
+ if (params.hasInputFeatureSpy()) {
+ outResult.mCustomizableCaptionRegion.set(captionInsetsRect);
+ }
+ final Resources resources = mDecorWindowContext.getResources();
+ boundingRects = new Rect[numOfElements];
+ for (int i = 0; i < numOfElements; i++) {
+ final OccludingCaptionElement element =
+ params.mOccludingCaptionElements.get(i);
+ final int elementWidthPx =
+ resources.getDimensionPixelSize(element.mWidthResId);
+ boundingRects[i] =
+ calculateBoundingRect(element, elementWidthPx, captionInsetsRect);
+ // Subtract the regions used by the caption elements, the rest is
+ // customizable.
+ if (params.hasInputFeatureSpy()) {
+ outResult.mCustomizableCaptionRegion.op(boundingRects[i],
+ Region.Op.DIFFERENCE);
+ }
+ }
}
+ final WindowDecorationInsets newInsets = new WindowDecorationInsets(
+ mTaskInfo.token, mOwner, captionInsetsRect, boundingRects);
+ if (!newInsets.equals(mWindowDecorationInsets)) {
+ // Add or update this caption as an insets source.
+ mWindowDecorationInsets = newInsets;
+ mWindowDecorationInsets.addOrUpdate(wct);
+ }
+ }
+
+ private void updateTaskSurface(RelayoutParams params, SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT, RelayoutResult<T> outResult) {
if (params.mSetTaskPositionAndCrop) {
+ final Point taskPosition = mTaskInfo.positionInParent;
startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
finishT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight)
.setPosition(mTaskSurface, taskPosition.x, taskPosition.y);
}
- startT.setShadowRadius(mTaskSurface, shadowRadius)
- .show(mTaskSurface);
+ float shadowRadius;
+ if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+ // Shadow is not needed for fullscreen tasks
+ shadowRadius = 0;
+ } else {
+ shadowRadius =
+ loadDimension(mDecorWindowContext.getResources(), params.mShadowRadiusId);
+ }
+ startT.setShadowRadius(mTaskSurface, shadowRadius).show(mTaskSurface);
finishT.setShadowRadius(mTaskSurface, shadowRadius);
+
if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
if (!DesktopModeStatus.isVeiledResizeEnabled()) {
// When fluid resize is enabled, add a background to freeform tasks
@@ -390,7 +408,10 @@
} else if (!DesktopModeStatus.isVeiledResizeEnabled()) {
startT.unsetColor(mTaskSurface);
}
+ }
+ private void updateViewHost(RelayoutParams params, SurfaceControl.Transaction onDrawTransaction,
+ RelayoutResult<T> outResult) {
Trace.beginSection("CaptionViewHostLayout");
if (mCaptionWindowManager == null) {
// Put caption under a container surface because ViewRootImpl sets the destination frame
@@ -399,9 +420,7 @@
mTaskInfo.getConfiguration(), mCaptionContainerSurface,
null /* hostInputToken */);
}
-
- // Caption view
- mCaptionWindowManager.setConfiguration(taskConfig);
+ mCaptionWindowManager.setConfiguration(mTaskInfo.getConfiguration());
final WindowManager.LayoutParams lp =
new WindowManager.LayoutParams(outResult.mCaptionWidth, outResult.mCaptionHeight,
TYPE_APPLICATION,
@@ -414,14 +433,14 @@
mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay,
mCaptionWindowManager);
if (params.mApplyStartTransactionOnDraw) {
- mViewHost.getRootSurfaceControl().applyTransactionOnDraw(startT);
+ mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction);
}
mViewHost.setView(outResult.mRootView, lp);
Trace.endSection();
} else {
Trace.beginSection("CaptionViewHostLayout-relayout");
if (params.mApplyStartTransactionOnDraw) {
- mViewHost.getRootSurfaceControl().applyTransactionOnDraw(startT);
+ mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction);
}
mViewHost.relayout(lp);
Trace.endSection();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index f6f3aa4..1903586 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -123,6 +123,7 @@
private DefaultCrossActivityBackAnimation mDefaultCrossActivityBackAnimation;
private CrossTaskBackAnimation mCrossTaskBackAnimation;
private ShellBackAnimationRegistry mShellBackAnimationRegistry;
+ private Rect mTouchableRegion;
@Before
public void setUp() throws Exception {
@@ -158,6 +159,8 @@
mShellCommandHandler);
mShellInit.init();
mShellExecutor.flushAll();
+ mTouchableRegion = new Rect(0, 0, 100, 100);
+ mController.mTouchableArea.set(mTouchableRegion);
}
private void createNavigationInfo(int backType,
@@ -169,7 +172,8 @@
.setOnBackNavigationDone(new RemoteCallback((bundle) -> {}))
.setOnBackInvokedCallback(mAppCallback)
.setPrepareRemoteAnimation(enableAnimation)
- .setAnimationCallback(isAnimationCallback);
+ .setAnimationCallback(isAnimationCallback)
+ .setTouchableRegion(mTouchableRegion);
createNavigationInfo(builder);
}
@@ -234,7 +238,8 @@
.setType(type)
.setOnBackInvokedCallback(mAppCallback)
.setPrepareRemoteAnimation(true)
- .setOnBackNavigationDone(new RemoteCallback(result)));
+ .setOnBackNavigationDone(new RemoteCallback(result))
+ .setTouchableRegion(mTouchableRegion));
triggerBackGesture();
simulateRemoteAnimationStart();
mShellExecutor.flushAll();
@@ -512,7 +517,8 @@
.setType(type)
.setOnBackInvokedCallback(mAppCallback)
.setPrepareRemoteAnimation(true)
- .setOnBackNavigationDone(new RemoteCallback(result)));
+ .setOnBackNavigationDone(new RemoteCallback(result))
+ .setTouchableRegion(mTouchableRegion));
triggerBackGesture();
simulateRemoteAnimationStart();
mShellExecutor.flushAll();
@@ -543,7 +549,8 @@
createNavigationInfo(new BackNavigationInfo.Builder()
.setType(type)
.setOnBackInvokedCallback(mAppCallback)
- .setOnBackNavigationDone(new RemoteCallback(result)));
+ .setOnBackNavigationDone(new RemoteCallback(result))
+ .setTouchableRegion(mTouchableRegion));
triggerBackGesture();
mShellExecutor.flushAll();
releaseBackGesture();
@@ -570,7 +577,8 @@
createNavigationInfo(new BackNavigationInfo.Builder()
.setType(type)
.setOnBackInvokedCallback(mAppCallback)
- .setOnBackNavigationDone(new RemoteCallback(result)));
+ .setOnBackNavigationDone(new RemoteCallback(result))
+ .setTouchableRegion(mTouchableRegion));
doMotionEvent(MotionEvent.ACTION_CANCEL, 0);
mShellExecutor.flushAll();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
index 7122181..5f6132a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
@@ -187,7 +187,6 @@
}
@Test
- // TODO(b/344822506): Update test when we add enter reason for app from overview
fun transitEnterDesktopFromAppFromOverview_logTaskAddedAndEnterReasonUnknown() {
val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
val transitionInfo =
@@ -200,7 +199,7 @@
assertThat(sessionId).isNotNull()
verify(desktopModeEventLogger, times(1))
- .logSessionEnter(eq(sessionId!!), eq(EnterReason.UNKNOWN_ENTER))
+ .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FROM_OVERVIEW))
verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
verifyZeroInteractions(desktopModeEventLogger)
}
diff --git a/location/lib/java/com/android/location/provider/SignificantPlaceProvider.java b/location/lib/java/com/android/location/provider/SignificantPlaceProvider.java
index 0b39a9a..df4b903 100644
--- a/location/lib/java/com/android/location/provider/SignificantPlaceProvider.java
+++ b/location/lib/java/com/android/location/provider/SignificantPlaceProvider.java
@@ -21,17 +21,22 @@
import android.hardware.location.ISignificantPlaceProvider;
import android.hardware.location.ISignificantPlaceProviderManager;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
+import android.util.Log;
import com.android.internal.annotations.GuardedBy;
/** @hide */
-public class SignificantPlaceProvider {
+public abstract class SignificantPlaceProvider {
public static final String ACTION = TrustManager.ACTION_BIND_SIGNIFICANT_PLACE_PROVIDER;
+ private static final String TAG = "SignificantPlaceProvider";
+
private final IBinder mBinder;
// write locked on mBinder, read lock is optional depending on atomicity requirements
@@ -69,6 +74,9 @@
}
}
+ /** Invoked when some client has checked whether the device is in a significant place. */
+ public abstract void onSignificantPlaceCheck();
+
private final class Service extends ISignificantPlaceProvider.Stub {
Service() {}
@@ -76,7 +84,7 @@
@Override
public void setSignificantPlaceProviderManager(ISignificantPlaceProviderManager manager) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- return;
+ throw new SecurityException();
}
synchronized (mBinder) {
@@ -91,5 +99,22 @@
mManager = manager;
}
}
+
+ @Override
+ public void onSignificantPlaceCheck() {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException();
+ }
+
+ try {
+ SignificantPlaceProvider.this.onSignificantPlaceCheck();
+ } catch (RuntimeException e) {
+ // exceptions on one-way binder threads are dropped - move to a different thread
+ Log.w(TAG, e);
+ new Handler(Looper.getMainLooper()).post(() -> {
+ throw new AssertionError(e);
+ });
+ }
+ }
}
}
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index 8ea4632..746c280 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -111,6 +111,7 @@
"allocator_may_return_null = 1",
],
},
+ dictionary: "fuzz/imagedecoder_fuzzer.dict",
host_supported: true,
}
diff --git a/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp b/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp
index 838bf3f..6743997 100644
--- a/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp
+++ b/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp
@@ -15,32 +15,15 @@
*/
#include <android/imagedecoder.h>
-
#include <binder/IPCThreadState.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <cstdlib>
-#include <memory>
+#include <fuzzer/FuzzedDataProvider.h>
#ifdef PNG_MUTATOR_DEFINE_LIBFUZZER_CUSTOM_MUTATOR
#include <fuzz/png_mutator.h>
#endif
-struct DecoderDeleter {
- void operator()(AImageDecoder* decoder) const { AImageDecoder_delete(decoder); }
-};
-
-using DecoderPointer = std::unique_ptr<AImageDecoder, DecoderDeleter>;
-
-static DecoderPointer makeDecoder(const uint8_t* data, size_t size) {
- AImageDecoder* decoder = nullptr;
- int result = AImageDecoder_createFromBuffer(data, size, &decoder);
- if (result != ANDROID_IMAGE_DECODER_SUCCESS) {
- // This was not a valid image.
- return nullptr;
- }
- return DecoderPointer(decoder);
-}
+constexpr int32_t kMaxDimension = 5000;
+constexpr int32_t kMinDimension = 0;
struct PixelFreer {
void operator()(void* pixels) const { std::free(pixels); }
@@ -48,41 +31,113 @@
using PixelPointer = std::unique_ptr<void, PixelFreer>;
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- // Without this call, decoding HEIF may time out on binder IPC calls.
- android::ProcessState::self()->startThreadPool();
+AImageDecoder* init(const uint8_t* data, size_t size, bool useFileDescriptor) {
+ AImageDecoder* decoder = nullptr;
+ if (useFileDescriptor) {
+ constexpr char testFd[] = "tempFd";
+ int32_t fileDesc = open(testFd, O_RDWR | O_CREAT | O_TRUNC);
+ write(fileDesc, data, size);
+ AImageDecoder_createFromFd(fileDesc, &decoder);
+ close(fileDesc);
+ } else {
+ AImageDecoder_createFromBuffer(data, size, &decoder);
+ }
+ return decoder;
+}
- DecoderPointer decoder = makeDecoder(data, size);
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider = FuzzedDataProvider(data, size);
+ /**
+ * Use maximum of 80% of buffer for creating decoder and save at least
+ * 20% buffer for fuzzing other APIs
+ */
+ const int32_t dataSize = dataProvider.ConsumeIntegralInRange<int32_t>(0, (size * 80) / 100);
+ std::vector<uint8_t> inputBuffer = dataProvider.ConsumeBytes<uint8_t>(dataSize);
+ AImageDecoder* decoder =
+ init(inputBuffer.data(), inputBuffer.size(), dataProvider.ConsumeBool());
if (!decoder) {
return 0;
}
-
- const AImageDecoderHeaderInfo* info = AImageDecoder_getHeaderInfo(decoder.get());
- int32_t width = AImageDecoderHeaderInfo_getWidth(info);
- int32_t height = AImageDecoderHeaderInfo_getHeight(info);
-
- // Set an arbitrary limit on the size of an image. The fuzzer runs with a
- // limited amount of memory, and keeping this allocation small allows the
- // fuzzer to continue running to try to find more serious problems. This
- // size is large enough to hold a photo taken by a current gen phone.
- constexpr int32_t kMaxDimension = 5000;
- if (width > kMaxDimension || height > kMaxDimension) {
- return 0;
+ const AImageDecoderHeaderInfo* headerInfo = AImageDecoder_getHeaderInfo(decoder);
+ AImageDecoderFrameInfo* frameInfo = AImageDecoderFrameInfo_create();
+ int32_t height = AImageDecoderHeaderInfo_getHeight(headerInfo);
+ int32_t width = AImageDecoderHeaderInfo_getWidth(headerInfo);
+ while (dataProvider.remaining_bytes()) {
+ auto invokeImageApi = dataProvider.PickValueInArray<const std::function<void()>>({
+ [&]() {
+ int32_t testHeight =
+ dataProvider.ConsumeIntegralInRange<int32_t>(kMinDimension,
+ kMaxDimension);
+ int32_t testWidth = dataProvider.ConsumeIntegralInRange<int32_t>(kMinDimension,
+ kMaxDimension);
+ int32_t result = AImageDecoder_setTargetSize(decoder, testHeight, testWidth);
+ if (result == ANDROID_IMAGE_DECODER_SUCCESS) {
+ height = testHeight;
+ width = testWidth;
+ }
+ },
+ [&]() {
+ const bool required = dataProvider.ConsumeBool();
+ AImageDecoder_setUnpremultipliedRequired(decoder, required);
+ },
+ [&]() {
+ AImageDecoder_setAndroidBitmapFormat(
+ decoder,
+ dataProvider.ConsumeIntegralInRange<
+ int32_t>(ANDROID_BITMAP_FORMAT_NONE,
+ ANDROID_BITMAP_FORMAT_RGBA_1010102) /* format */);
+ },
+ [&]() {
+ AImageDecoder_setDataSpace(decoder,
+ dataProvider
+ .ConsumeIntegral<int32_t>() /* dataspace */);
+ },
+ [&]() {
+ ARect rect{dataProvider.ConsumeIntegral<int32_t>() /* left */,
+ dataProvider.ConsumeIntegral<int32_t>() /* top */,
+ dataProvider.ConsumeIntegral<int32_t>() /* right */,
+ dataProvider.ConsumeIntegral<int32_t>() /* bottom */};
+ AImageDecoder_setCrop(decoder, rect);
+ },
+ [&]() { AImageDecoderHeaderInfo_getWidth(headerInfo); },
+ [&]() { AImageDecoderHeaderInfo_getMimeType(headerInfo); },
+ [&]() { AImageDecoderHeaderInfo_getAlphaFlags(headerInfo); },
+ [&]() { AImageDecoderHeaderInfo_getAndroidBitmapFormat(headerInfo); },
+ [&]() {
+ int32_t tempHeight;
+ int32_t tempWidth;
+ AImageDecoder_computeSampledSize(decoder,
+ dataProvider.ConsumeIntegral<
+ int>() /* sampleSize */,
+ &tempWidth, &tempHeight);
+ },
+ [&]() { AImageDecoderHeaderInfo_getDataSpace(headerInfo); },
+ [&]() { AImageDecoder_getRepeatCount(decoder); },
+ [&]() { AImageDecoder_getFrameInfo(decoder, frameInfo); },
+ [&]() { AImageDecoderFrameInfo_getDuration(frameInfo); },
+ [&]() { AImageDecoderFrameInfo_hasAlphaWithinBounds(frameInfo); },
+ [&]() { AImageDecoderFrameInfo_getDisposeOp(frameInfo); },
+ [&]() { AImageDecoderFrameInfo_getBlendOp(frameInfo); },
+ [&]() {
+ AImageDecoder_setInternallyHandleDisposePrevious(
+ decoder, dataProvider.ConsumeBool() /* handle */);
+ },
+ [&]() { AImageDecoder_rewind(decoder); },
+ [&]() { AImageDecoder_advanceFrame(decoder); },
+ [&]() {
+ size_t stride = AImageDecoder_getMinimumStride(decoder);
+ size_t pixelSize = height * stride;
+ auto pixels = PixelPointer(std::malloc(pixelSize));
+ if (!pixels.get()) {
+ return;
+ }
+ AImageDecoder_decodeImage(decoder, pixels.get(), stride, pixelSize);
+ },
+ });
+ invokeImageApi();
}
- size_t stride = AImageDecoder_getMinimumStride(decoder.get());
- size_t pixelSize = height * stride;
- auto pixels = PixelPointer(std::malloc(pixelSize));
- if (!pixels.get()) {
- return 0;
- }
-
- while (true) {
- int result = AImageDecoder_decodeImage(decoder.get(), pixels.get(), stride, pixelSize);
- if (result != ANDROID_IMAGE_DECODER_SUCCESS) break;
-
- result = AImageDecoder_advanceFrame(decoder.get());
- if (result != ANDROID_IMAGE_DECODER_SUCCESS) break;
- }
+ AImageDecoderFrameInfo_delete(frameInfo);
+ AImageDecoder_delete(decoder);
return 0;
}
diff --git a/native/graphics/jni/fuzz/imagedecoder_fuzzer.dict b/native/graphics/jni/fuzz/imagedecoder_fuzzer.dict
new file mode 100644
index 0000000..5b54a0e
--- /dev/null
+++ b/native/graphics/jni/fuzz/imagedecoder_fuzzer.dict
@@ -0,0 +1,7 @@
+kw1="\x89\x50\x4E\x47"
+kw2="\xff\xD8\xFF"
+kw4="\x52\x49\x46\x46"
+kw5="\x00\x00\x01\x00"
+kw6="\x47\x49\x46\x08"
+kw7="ftyp"
+kw8="\x04\x00\x00\x00"
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java b/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java
index f3ff0fe..717a8ee 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java
@@ -34,6 +34,7 @@
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.media.MediaRoute2Info;
+import android.os.SystemProperties;
import android.util.SparseIntArray;
import androidx.annotation.NonNull;
@@ -42,6 +43,7 @@
import com.android.settingslib.R;
import com.android.settingslib.media.flags.Flags;
+import java.util.Arrays;
import java.util.Objects;
/** A util class to get the appropriate icon for different device types. */
@@ -50,18 +52,23 @@
private static final SparseIntArray AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE = new SparseIntArray();
private final boolean mIsTv;
+ private final boolean mIsTablet;
private final Context mContext;
public DeviceIconUtil(@NonNull Context context) {
mContext = Objects.requireNonNull(context);
mIsTv =
mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)
&& Flags.enableTvMediaOutputDialog();
+ mIsTablet =
+ Arrays.asList(SystemProperties.get("ro.build.characteristics").split(","))
+ .contains("tablet");
}
@VisibleForTesting
/* package */ DeviceIconUtil(boolean isTv) {
mContext = null;
mIsTv = isTv;
+ mIsTablet = false;
}
/** Returns a drawable for an icon representing the given audioDeviceType. */
@@ -80,12 +87,17 @@
/** Returns a drawable res ID for an icon representing the given mediaRouteType. */
@DrawableRes
public int getIconResIdFromMediaRouteType(@MediaRoute2Info.Type int type) {
- return mIsTv ? getIconResourceIdForTv(type) : getIconResourceIdForPhone(type);
+ return mIsTv
+ ? getIconResourceIdForTv(type)
+ : getIconResourceIdForPhoneOrTablet(type, mIsTablet);
}
@SuppressLint("SwitchIntDef")
@DrawableRes
- private static int getIconResourceIdForPhone(@MediaRoute2Info.Type int type) {
+ private static int getIconResourceIdForPhoneOrTablet(
+ @MediaRoute2Info.Type int type, boolean isTablet) {
+ int defaultResId = isTablet ? R.drawable.ic_media_tablet : R.drawable.ic_smartphone;
+
return switch (type) {
case MediaRoute2Info.TYPE_USB_DEVICE,
MediaRoute2Info.TYPE_USB_HEADSET,
@@ -98,7 +110,7 @@
MediaRoute2Info.TYPE_HDMI_ARC,
MediaRoute2Info.TYPE_HDMI_EARC ->
R.drawable.ic_external_display;
- default -> R.drawable.ic_smartphone; // Includes TYPE_BUILTIN_SPEAKER.
+ default -> defaultResId; // Includes TYPE_BUILTIN_SPEAKER.
};
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java
index 8edda1a..883640d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
+import android.content.Context;
import android.media.AudioDeviceInfo;
import android.media.MediaRoute2Info;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -30,6 +31,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowSystemProperties;
@RunWith(RobolectricTestRunner.class)
public class DeviceIconUtilTest {
@@ -37,9 +40,12 @@
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ private Context mContext;
+
@Before
public void setup() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TV_MEDIA_OUTPUT_DIALOG);
+ mContext = RuntimeEnvironment.getApplication();
}
@Test
@@ -171,6 +177,14 @@
}
@Test
+ public void getIconResIdFromMediaRouteType_onTablet_builtinSpeaker_isTablet() {
+ ShadowSystemProperties.override("ro.build.characteristics", "tablet");
+ assertThat(new DeviceIconUtil(mContext)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_BUILTIN_SPEAKER))
+ .isEqualTo(R.drawable.ic_media_tablet);
+ }
+
+ @Test
public void getIconResIdFromMediaRouteType_unsupportedType_isSmartphone() {
assertThat(new DeviceIconUtil(/* isTv */ false)
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_UNKNOWN))
@@ -178,6 +192,14 @@
}
@Test
+ public void getIconResIdFromMediaRouteType_onTablet_unsupportedType_isTablet() {
+ ShadowSystemProperties.override("ro.build.characteristics", "tablet");
+ assertThat(new DeviceIconUtil(mContext)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_UNKNOWN))
+ .isEqualTo(R.drawable.ic_media_tablet);
+ }
+
+ @Test
public void getIconResIdFromMediaRouteType_tv_unsupportedType_isSpeaker() {
assertThat(new DeviceIconUtil(/* isTv */ true)
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_UNKNOWN))
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index fde7c2c..58c39b4 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -785,7 +785,6 @@
kotlincflags: ["-Xjvm-default=all"],
optimize: {
shrink_resources: false,
- optimized_shrink_resources: false,
proguard_flags_files: ["proguard.flags"],
},
@@ -922,7 +921,6 @@
optimize: true,
shrink: true,
shrink_resources: true,
- optimized_shrink_resources: true,
ignore_warnings: false,
proguard_compatibility: false,
},
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index a9c4399..bd6efe5 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -650,6 +650,7 @@
<!-- started from MediaProjectionManager -->
<activity
android:name=".mediaprojection.permission.MediaProjectionPermissionActivity"
+ android:showForAllUsers="true"
android:exported="true"
android:theme="@style/Theme.SystemUI.MediaProjectionAlertDialog"
android:finishOnCloseSystemDialogs="true"
@@ -660,6 +661,7 @@
<activity
android:name=".mediaprojection.appselector.MediaProjectionAppSelectorActivity"
android:theme="@style/Theme.SystemUI.MediaProjectionAppSelector"
+ android:showForAllUsers="true"
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true"
android:documentLaunchMode="never"
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
index 9891b5b..3295dde 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
@@ -17,6 +17,7 @@
package com.android.systemui.volume.panel.component.spatialaudio.ui.composable
import android.view.Gravity
+import androidx.annotation.VisibleForTesting
import androidx.compose.foundation.basicMarquee
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
@@ -71,7 +72,8 @@
}
@Composable
- private fun Content(dialog: SystemUIDialog) {
+ @VisibleForTesting
+ fun Content(dialog: SystemUIDialog) {
val isAvailable by viewModel.isAvailable.collectAsStateWithLifecycle()
if (!isAvailable) {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
index 3cc8431..6001f1f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
@@ -19,8 +19,6 @@
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.awaitHorizontalTouchSlopOrCancellation
import androidx.compose.foundation.gestures.awaitVerticalTouchSlopOrCancellation
-import androidx.compose.foundation.gestures.horizontalDrag
-import androidx.compose.foundation.gestures.verticalDrag
import androidx.compose.runtime.Stable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
@@ -32,7 +30,9 @@
import androidx.compose.ui.input.pointer.PointerInputScope
import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode
import androidx.compose.ui.input.pointer.changedToDownIgnoreConsumed
+import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
import androidx.compose.ui.input.pointer.positionChange
+import androidx.compose.ui.input.pointer.positionChangeIgnoreConsumed
import androidx.compose.ui.input.pointer.util.VelocityTracker
import androidx.compose.ui.input.pointer.util.addPointerInputChange
import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
@@ -46,6 +46,8 @@
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.util.fastAll
+import androidx.compose.ui.util.fastAny
+import androidx.compose.ui.util.fastFirstOrNull
import androidx.compose.ui.util.fastForEach
import kotlin.coroutines.cancellation.CancellationException
import kotlin.math.sign
@@ -236,8 +238,23 @@
onDragCancel: (controller: DragController) -> Unit,
swipeDetector: SwipeDetector,
) {
- // Wait for a consumable event in [PointerEventPass.Main] pass
- val consumablePointer = awaitConsumableEvent().changes.first()
+ val consumablePointer =
+ awaitConsumableEvent {
+ // We are searching for an event that can be used as the starting point for the
+ // drag gesture. Our options are:
+ // - Initial: These events should never be consumed by the MultiPointerDraggable
+ // since our ancestors can consume the gesture, but we would eliminate this
+ // possibility for our descendants.
+ // - Main: These events are consumed during the drag gesture, and they are a
+ // good place to start if the previous event has not been consumed.
+ // - Final: If the previous event has been consumed, we can wait for the Main
+ // pass to finish. If none of our ancestors were interested in the event, we
+ // can wait for an unconsumed event in the Final pass.
+ val previousConsumed = currentEvent.changes.fastAny { it.isConsumed }
+ if (previousConsumed) PointerEventPass.Final else PointerEventPass.Main
+ }
+ .changes
+ .first()
var overSlop = 0f
val drag =
@@ -297,18 +314,22 @@
onDrag(controller, drag, overSlop)
successful =
- when (orientation) {
- Orientation.Horizontal ->
- horizontalDrag(drag.id) {
- onDrag(controller, it, it.positionChange().toFloat())
- it.consume()
- }
- Orientation.Vertical ->
- verticalDrag(drag.id) {
- onDrag(controller, it, it.positionChange().toFloat())
- it.consume()
- }
- }
+ drag(
+ initialPointerId = drag.id,
+ hasDragged = { it.positionChangeIgnoreConsumed().toFloat() != 0f },
+ onDrag = {
+ onDrag(controller, it, it.positionChange().toFloat())
+ it.consume()
+ },
+ onIgnoredEvent = {
+ // We are still dragging an object, but this event is not of interest to
+ // the caller.
+ // This event will not trigger the onDrag event, but we will consume the
+ // event to prevent another pointerInput from interrupting the current
+ // gesture just because the event was ignored.
+ it.consume()
+ },
+ )
} catch (t: Throwable) {
onDragCancel(controller)
throw t
@@ -322,7 +343,9 @@
}
}
- private suspend fun AwaitPointerEventScope.awaitConsumableEvent(): PointerEvent {
+ private suspend fun AwaitPointerEventScope.awaitConsumableEvent(
+ pass: () -> PointerEventPass,
+ ): PointerEvent {
fun canBeConsumed(changes: List<PointerInputChange>): Boolean {
// All pointers must be:
return changes.fastAll {
@@ -337,9 +360,7 @@
var event: PointerEvent
do {
- // To allow the descendants with the opportunity to consume the event, we wait for it in
- // the Main pass.
- event = awaitPointerEvent()
+ event = awaitPointerEvent(pass = pass())
} while (!canBeConsumed(event.changes))
// We found a consumable event in the Main pass
@@ -352,4 +373,82 @@
Orientation.Horizontal -> x
}
}
+
+ /**
+ * Continues to read drag events until all pointers are up or the drag event is canceled. The
+ * initial pointer to use for driving the drag is [initialPointerId]. [hasDragged] passes the
+ * result whether a change was detected from the drag function or not.
+ *
+ * Whenever the pointer moves, if [hasDragged] returns true, [onDrag] is called; otherwise,
+ * [onIgnoredEvent] is called.
+ *
+ * @return true when gesture ended with all pointers up and false when the gesture was canceled.
+ *
+ * Note: Inspired by DragGestureDetector.kt
+ */
+ private suspend inline fun AwaitPointerEventScope.drag(
+ initialPointerId: PointerId,
+ hasDragged: (PointerInputChange) -> Boolean,
+ onDrag: (PointerInputChange) -> Unit,
+ onIgnoredEvent: (PointerInputChange) -> Unit,
+ ): Boolean {
+ val pointer = currentEvent.changes.fastFirstOrNull { it.id == initialPointerId }
+ val isPointerUp = pointer?.pressed != true
+ if (isPointerUp) {
+ return false // The pointer has already been lifted, so the gesture is canceled
+ }
+ var pointerId = initialPointerId
+ while (true) {
+ val change = awaitDragOrUp(pointerId, hasDragged, onIgnoredEvent) ?: return false
+
+ if (change.isConsumed) {
+ return false
+ }
+
+ if (change.changedToUpIgnoreConsumed()) {
+ return true
+ }
+
+ onDrag(change)
+ pointerId = change.id
+ }
+ }
+
+ /**
+ * Waits for a single drag in one axis, final pointer up, or all pointers are up. When
+ * [initialPointerId] has lifted, another pointer that is down is chosen to be the finger
+ * governing the drag. When the final pointer is lifted, that [PointerInputChange] is returned.
+ * When a drag is detected, that [PointerInputChange] is returned. A drag is only detected when
+ * [hasDragged] returns `true`. Events that should not be captured are passed to
+ * [onIgnoredEvent].
+ *
+ * `null` is returned if there was an error in the pointer input stream and the pointer that was
+ * down was dropped before the 'up' was received.
+ *
+ * Note: Inspired by DragGestureDetector.kt
+ */
+ private suspend inline fun AwaitPointerEventScope.awaitDragOrUp(
+ initialPointerId: PointerId,
+ hasDragged: (PointerInputChange) -> Boolean,
+ onIgnoredEvent: (PointerInputChange) -> Unit,
+ ): PointerInputChange? {
+ var pointerId = initialPointerId
+ while (true) {
+ val event = awaitPointerEvent()
+ val dragEvent = event.changes.fastFirstOrNull { it.id == pointerId } ?: return null
+ if (dragEvent.changedToUpIgnoreConsumed()) {
+ val otherDown = event.changes.fastFirstOrNull { it.pressed }
+ if (otherDown == null) {
+ // This is the last "up"
+ return dragEvent
+ } else {
+ pointerId = otherDown.id
+ }
+ } else if (hasDragged(dragEvent)) {
+ return dragEvent
+ } else {
+ onIgnoredEvent(dragEvent)
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
index 4bb643f..1a0740b 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
@@ -349,6 +349,121 @@
}
@Test
+ fun multiPointerDuringAnotherGestureWaitAConsumableEventAfterMainPass() {
+ val size = 200f
+ val middle = Offset(size / 2f, size / 2f)
+
+ var verticalStarted = false
+ var verticalDragged = false
+ var verticalStopped = false
+ var horizontalStarted = false
+ var horizontalDragged = false
+ var horizontalStopped = false
+
+ var touchSlop = 0f
+ rule.setContent {
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ Box(
+ Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() })
+ .multiPointerDraggable(
+ orientation = Orientation.Vertical,
+ enabled = { true },
+ startDragImmediately = { false },
+ onDragStarted = { _, _, _ ->
+ verticalStarted = true
+ object : DragController {
+ override fun onDrag(delta: Float) {
+ verticalDragged = true
+ }
+
+ override fun onStop(velocity: Float, canChangeScene: Boolean) {
+ verticalStopped = true
+ }
+ }
+ },
+ )
+ .multiPointerDraggable(
+ orientation = Orientation.Horizontal,
+ enabled = { true },
+ startDragImmediately = { false },
+ onDragStarted = { _, _, _ ->
+ horizontalStarted = true
+ object : DragController {
+ override fun onDrag(delta: Float) {
+ horizontalDragged = true
+ }
+
+ override fun onStop(velocity: Float, canChangeScene: Boolean) {
+ horizontalStopped = true
+ }
+ }
+ },
+ )
+ )
+ }
+
+ fun startDraggingDown() {
+ rule.onRoot().performTouchInput {
+ down(middle)
+ moveBy(Offset(0f, touchSlop))
+ }
+ }
+
+ fun startDraggingRight() {
+ rule.onRoot().performTouchInput {
+ down(middle)
+ moveBy(Offset(touchSlop, 0f))
+ }
+ }
+
+ fun stopDragging() {
+ rule.onRoot().performTouchInput { up() }
+ }
+
+ fun continueDown() {
+ rule.onRoot().performTouchInput { moveBy(Offset(0f, touchSlop)) }
+ }
+
+ fun continueRight() {
+ rule.onRoot().performTouchInput { moveBy(Offset(touchSlop, 0f)) }
+ }
+
+ startDraggingDown()
+ assertThat(verticalStarted).isTrue()
+ assertThat(verticalDragged).isTrue()
+ assertThat(verticalStopped).isFalse()
+
+ // Ignore right swipe, do not interrupt the dragging gesture.
+ continueRight()
+ assertThat(horizontalStarted).isFalse()
+ assertThat(horizontalDragged).isFalse()
+ assertThat(horizontalStopped).isFalse()
+ assertThat(verticalStopped).isFalse()
+
+ stopDragging()
+ assertThat(verticalStopped).isTrue()
+
+ verticalStarted = false
+ verticalDragged = false
+ verticalStopped = false
+
+ startDraggingRight()
+ assertThat(horizontalStarted).isTrue()
+ assertThat(horizontalDragged).isTrue()
+ assertThat(horizontalStopped).isFalse()
+
+ // Ignore down swipe, do not interrupt the dragging gesture.
+ continueDown()
+ assertThat(verticalStarted).isFalse()
+ assertThat(verticalDragged).isFalse()
+ assertThat(verticalStopped).isFalse()
+ assertThat(horizontalStopped).isFalse()
+
+ stopDragging()
+ assertThat(horizontalStopped).isTrue()
+ }
+
+ @Test
fun multiPointerSwipeDetectorInteraction() {
val size = 200f
val middle = Offset(size / 2f, size / 2f)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
index 04b930e..07d8890 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
@@ -25,12 +25,15 @@
import static org.mockito.Mockito.when;
import android.app.DreamManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.view.GestureDetector;
import android.view.MotionEvent;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shared.system.InputChannelCompat;
@@ -87,7 +90,7 @@
assertThat(captured).isTrue();
}
- // Verifies that a swipe in the upward direction is not catpured.
+ // Verifies that a swipe in the upward direction is not captured.
@Test
public void testSwipeUp_notCaptured() {
final boolean captured = swipe(Direction.UP);
@@ -98,34 +101,58 @@
// Verifies that a swipe down forwards captured touches to central surfaces for handling.
@Test
- public void testSwipeDown_sentToCentralSurfaces() {
+ @EnableFlags(Flags.FLAG_COMMUNAL_HUB)
+ public void testSwipeDown_communalEnabled_sentToCentralSurfaces() {
swipe(Direction.DOWN);
- // Both motion events are sent for the shade window to process.
+ // Both motion events are sent for central surfaces to process.
verify(mCentralSurfaces, times(2)).handleExternalShadeWindowTouch(any());
}
- // Verifies that a swipe down forwards captured touches to central surfaces for handling.
+ // Verifies that a swipe down forwards captured touches to the shade view for handling.
+ @Test
+ @DisableFlags(Flags.FLAG_COMMUNAL_HUB)
+ public void testSwipeDown_communalDisabled_sentToShadeView() {
+ swipe(Direction.DOWN);
+
+ // Both motion events are sent for the shade view to process.
+ verify(mShadeViewController, times(2)).handleExternalTouch(any());
+ }
+
+ // Verifies that a swipe down while dreaming forwards captured touches to the shade view for
+ // handling.
@Test
public void testSwipeDown_dreaming_sentToShadeView() {
when(mDreamManager.isDreaming()).thenReturn(true);
swipe(Direction.DOWN);
- // Both motion events are sent for the shade window to process.
+ // Both motion events are sent for the shade view to process.
verify(mShadeViewController, times(2)).handleExternalTouch(any());
}
- // Verifies that a swipe down is not forwarded to the shade window.
+ // Verifies that a swipe up is not forwarded to central surfaces.
@Test
- public void testSwipeUp_touchesNotSent() {
+ @EnableFlags(Flags.FLAG_COMMUNAL_HUB)
+ public void testSwipeUp_communalEnabled_touchesNotSent() {
swipe(Direction.UP);
- // Motion events are not sent for the shade window to process as the swipe is going in the
+ // Motion events are not sent for central surfaces to process as the swipe is going in the
// wrong direction.
verify(mCentralSurfaces, never()).handleExternalShadeWindowTouch(any());
}
+ // Verifies that a swipe up is not forwarded to the shade view.
+ @Test
+ @DisableFlags(Flags.FLAG_COMMUNAL_HUB)
+ public void testSwipeUp_communalDisabled_touchesNotSent() {
+ swipe(Direction.UP);
+
+ // Motion events are not sent for the shade view to process as the swipe is going in the
+ // wrong direction.
+ verify(mShadeViewController, never()).handleExternalTouch(any());
+ }
+
/**
* Simulates a swipe in the given direction and returns true if the touch was intercepted by the
* touch handler's gesture listener.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index 4c8c113..c48ced1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.dreams;
import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
+import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -148,6 +149,8 @@
when(mDreamOverlayContainerView.getRootSurfaceControl())
.thenReturn(mAttachedSurfaceControl);
when(mKeyguardTransitionInteractor.isFinishedInStateWhere(any())).thenReturn(emptyFlow());
+ when(mShadeInteractor.isAnyExpanded()).thenReturn(MutableStateFlow(false));
+ when(mCommunalInteractor.isCommunalShowing()).thenReturn(MutableStateFlow(false));
mController = new DreamOverlayContainerViewController(
mDreamOverlayContainerView,
@@ -330,4 +333,12 @@
mController.onViewDetached();
verify(mBouncerlessScrimController).removeCallback(any());
}
+
+ @EnableFlags(android.service.dreams.Flags.FLAG_DREAM_HANDLES_BEING_OBSCURED)
+ @Test
+ public void testOnViewAttachedSucceedsWhenDreamHandlesBeingObscuredFlagEnabled() {
+ // This test will catch failures in presubmit when the dream_handles_being_obscured flag is
+ // enabled.
+ mController.onViewAttached();
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt
index 460a1fc..b0959e4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt
@@ -25,6 +25,7 @@
import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -58,6 +59,62 @@
assertThat(viewModel?.tint).isEqualTo(Color.WHITE)
}
+ @Test
+ fun startsDozing_doNotShowAodVariant() =
+ testScope.runTest {
+ val viewModel by collectLastValue(underTest.viewModel)
+
+ givenUdfpsEnrolledAndEnabled()
+ kosmos.run {
+ fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DOZING,
+ testScope = testScope,
+ throughTransitionState = TransitionState.STARTED,
+ )
+ }
+
+ assertThat(viewModel?.useAodVariant).isEqualTo(false)
+ }
+
+ @Test
+ fun finishedDozing_showAodVariant() =
+ testScope.runTest {
+ val viewModel by collectLastValue(underTest.viewModel)
+
+ givenUdfpsEnrolledAndEnabled()
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ testScope = testScope,
+ throughTransitionState = TransitionState.FINISHED,
+ )
+
+ assertThat(viewModel?.useAodVariant).isEqualTo(true)
+ }
+
+ @Test
+ fun startTransitionToLockscreenFromDozing_doNotShowAodVariant() =
+ testScope.runTest {
+ val viewModel by collectLastValue(underTest.viewModel)
+
+ givenUdfpsEnrolledAndEnabled()
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DOZING,
+ testScope = testScope,
+ throughTransitionState = TransitionState.FINISHED,
+ )
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.DOZING,
+ to = KeyguardState.LOCKSCREEN,
+ testScope = testScope,
+ throughTransitionState = TransitionState.RUNNING,
+ )
+
+ assertThat(viewModel?.useAodVariant).isEqualTo(false)
+ }
+
private fun givenUdfpsEnrolledAndEnabled() {
kosmos.fakeFingerprintPropertyRepository.supportsUdfps()
kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
index 68fbd1c..3f93401 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
@@ -227,11 +227,11 @@
assertThat(accessibilityDelegateHint)
.isEqualTo(DeviceEntryIconView.AccessibilityHintType.AUTHENTICATE)
- // non-interactive lock icon
+ // interactive lock icon for non udfps as well so that user can navigate to bouncer
fingerprintPropertyRepository.supportsRearFps()
assertThat(accessibilityDelegateHint)
- .isEqualTo(DeviceEntryIconView.AccessibilityHintType.NONE)
+ .isEqualTo(DeviceEntryIconView.AccessibilityHintType.AUTHENTICATE)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
index 7a37a9e..73e6506 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
@@ -56,11 +56,15 @@
underTest.addSelectedUserMediaEntry(userMedia)
assertThat(selectedUserEntries?.get(instanceId)).isEqualTo(userMedia)
+ assertThat(underTest.hasActiveMedia()).isTrue()
+ assertThat(underTest.hasAnyMedia()).isTrue()
underTest.addSelectedUserMediaEntry(userMedia.copy(active = false))
assertThat(selectedUserEntries?.get(instanceId)).isNotEqualTo(userMedia)
assertThat(selectedUserEntries?.get(instanceId)?.active).isFalse()
+ assertThat(underTest.hasActiveMedia()).isFalse()
+ assertThat(underTest.hasAnyMedia()).isTrue()
}
@Test
@@ -74,8 +78,12 @@
underTest.addSelectedUserMediaEntry(userMedia)
assertThat(selectedUserEntries?.get(instanceId)).isEqualTo(userMedia)
+ assertThat(underTest.hasActiveMedia()).isTrue()
+ assertThat(underTest.hasAnyMedia()).isTrue()
assertThat(underTest.removeSelectedUserMediaEntry(instanceId, userMedia)).isTrue()
+ assertThat(underTest.hasActiveMedia()).isFalse()
+ assertThat(underTest.hasAnyMedia()).isFalse()
}
@Test
@@ -145,6 +153,7 @@
assertThat(smartspaceMediaData).isNotEqualTo(mediaRecommendation)
assertThat(smartspaceMediaData?.isActive).isFalse()
+ assertThat(underTest.isRecommendationActive()).isFalse()
}
@Test
@@ -349,6 +358,14 @@
.inOrder()
}
+ @Test
+ fun hasAnyMedia_noMediaSet_returnsFalse() =
+ testScope.runTest { assertThat(underTest.hasAnyMedia()).isFalse() }
+
+ @Test
+ fun hasActiveMedia_noMediaSet_returnsFalse() =
+ testScope.runTest { assertThat(underTest.hasActiveMedia()).isFalse() }
+
private fun createMediaData(
app: String,
playing: Boolean,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
index 39dbc7e..c62195f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
@@ -76,22 +76,20 @@
testScope.runTest {
val hasActiveMediaOrRecommendation by
collectLastValue(underTest.hasActiveMediaOrRecommendation)
- val hasActiveMedia by collectLastValue(underTest.hasActiveMedia)
- val hasAnyMedia by collectLastValue(underTest.hasAnyMedia)
val userMedia = MediaData(active = true)
mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
assertThat(hasActiveMediaOrRecommendation).isTrue()
- assertThat(hasActiveMedia).isTrue()
- assertThat(hasAnyMedia).isTrue()
+ assertThat(underTest.hasActiveMedia()).isTrue()
+ assertThat(underTest.hasAnyMedia()).isTrue()
mediaFilterRepository.addSelectedUserMediaEntry(userMedia.copy(active = false))
assertThat(hasActiveMediaOrRecommendation).isFalse()
- assertThat(hasActiveMedia).isFalse()
- assertThat(hasAnyMedia).isTrue()
+ assertThat(underTest.hasActiveMedia()).isFalse()
+ assertThat(underTest.hasAnyMedia()).isTrue()
}
@Test
@@ -99,8 +97,6 @@
testScope.runTest {
val hasActiveMediaOrRecommendation by
collectLastValue(underTest.hasActiveMediaOrRecommendation)
- val hasActiveMedia by collectLastValue(underTest.hasActiveMedia)
- val hasAnyMedia by collectLastValue(underTest.hasAnyMedia)
val userMedia = MediaData(active = false)
val instanceId = userMedia.instanceId
@@ -109,8 +105,8 @@
mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
assertThat(hasActiveMediaOrRecommendation).isFalse()
- assertThat(hasActiveMedia).isFalse()
- assertThat(hasAnyMedia).isTrue()
+ assertThat(underTest.hasActiveMedia()).isFalse()
+ assertThat(underTest.hasAnyMedia()).isTrue()
assertThat(mediaFilterRepository.removeSelectedUserMediaEntry(instanceId, userMedia))
.isTrue()
@@ -119,8 +115,8 @@
)
assertThat(hasActiveMediaOrRecommendation).isFalse()
- assertThat(hasActiveMedia).isFalse()
- assertThat(hasAnyMedia).isFalse()
+ assertThat(underTest.hasActiveMedia()).isFalse()
+ assertThat(underTest.hasAnyMedia()).isFalse()
}
@Test
@@ -147,6 +143,7 @@
mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
mediaFilterRepository.addMediaDataLoadingState(mediaLoadingModel)
+ mediaFilterRepository.setOrderedMedia()
assertThat(hasActiveMediaOrRecommendation).isTrue()
assertThat(hasAnyMediaOrRecommendation).isTrue()
@@ -202,7 +199,7 @@
@Test
fun hasAnyMedia_noMediaSet_returnsFalse() =
- testScope.runTest { assertThat(underTest.hasAnyMedia.value).isFalse() }
+ testScope.runTest { assertThat(underTest.hasAnyMedia()).isFalse() }
@Test
fun hasAnyMediaOrRecommendation_noMediaSet_returnsFalse() =
@@ -210,7 +207,7 @@
@Test
fun hasActiveMedia_noMediaSet_returnsFalse() =
- testScope.runTest { assertThat(underTest.hasActiveMedia.value).isFalse() }
+ testScope.runTest { assertThat(underTest.hasActiveMedia()).isFalse() }
@Test
fun hasActiveMediaOrRecommendation_nothingSet_returnsFalse() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
index 10a4eb7..7385a47 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
@@ -71,6 +71,7 @@
addOverride(R.drawable.ic_headphone, testIcon)
addOverride(R.drawable.ic_smartphone, testIcon)
addOverride(R.drawable.ic_media_speaker_device, testIcon)
+ addOverride(R.drawable.ic_media_tablet, testIcon)
addOverride(com.android.internal.R.drawable.ic_bt_hearing_aid, testIcon)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt
index 8921a23..0f56d0b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt
@@ -65,6 +65,7 @@
with(context.orCreateTestableResources) {
addOverride(R.drawable.ic_smartphone, testIcon)
+ addOverride(R.drawable.ic_media_tablet, testIcon)
addOverride(R.string.media_transfer_this_device_name_tv, builtInDeviceName)
addOverride(R.string.media_transfer_this_device_name_tablet, builtInDeviceName)
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
index 0bd6d6e..3c4c003 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
@@ -30,7 +30,12 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Region;
+import android.os.Handler;
+import android.util.Log;
import android.view.AttachedSurfaceControl;
+import android.view.Display;
+import android.view.IRotationWatcher;
+import android.view.IWindowManager;
import android.view.LayoutInflater;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
@@ -46,15 +51,18 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.res.R;
+import com.android.systemui.util.leak.RotationUtils;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
class FullscreenMagnificationController implements ComponentCallbacks {
+ private static final String TAG = "FullscreenMagnificationController";
private final Context mContext;
private final AccessibilityManager mAccessibilityManager;
private final WindowManager mWindowManager;
+ private final IWindowManager mIWindowManager;
private Supplier<SurfaceControlViewHost> mScvhSupplier;
private SurfaceControlViewHost mSurfaceControlViewHost = null;
private SurfaceControl mBorderSurfaceControl = null;
@@ -65,33 +73,50 @@
private final int mDisplayId;
private static final Region sEmptyRegion = new Region();
private ValueAnimator mShowHideBorderAnimator;
+ private Handler mHandler;
private Executor mExecutor;
private boolean mFullscreenMagnificationActivated = false;
private final Configuration mConfiguration;
+ private final Runnable mShowBorderRunnable = this::showBorderWithNullCheck;
+ private int mRotation;
+ private final IRotationWatcher mRotationWatcher = new IRotationWatcher.Stub() {
+ @Override
+ public void onRotationChanged(final int rotation) {
+ handleScreenRotation();
+ }
+ };
+ private final long mLongAnimationTimeMs;
FullscreenMagnificationController(
@UiContext Context context,
- Executor executor,
+ @Main Handler handler,
+ @Main Executor executor,
AccessibilityManager accessibilityManager,
WindowManager windowManager,
+ IWindowManager iWindowManager,
Supplier<SurfaceControlViewHost> scvhSupplier) {
- this(context, executor, accessibilityManager, windowManager, scvhSupplier,
- new SurfaceControl.Transaction(), createNullTargetObjectAnimator(context));
+ this(context, handler, executor, accessibilityManager,
+ windowManager, iWindowManager, scvhSupplier,
+ new SurfaceControl.Transaction(), null);
}
@VisibleForTesting
FullscreenMagnificationController(
@UiContext Context context,
+ @Main Handler handler,
@Main Executor executor,
AccessibilityManager accessibilityManager,
WindowManager windowManager,
+ IWindowManager iWindowManager,
Supplier<SurfaceControlViewHost> scvhSupplier,
SurfaceControl.Transaction transaction,
ValueAnimator valueAnimator) {
mContext = context;
+ mHandler = handler;
mExecutor = executor;
mAccessibilityManager = accessibilityManager;
mWindowManager = windowManager;
+ mIWindowManager = iWindowManager;
mWindowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
mTransaction = transaction;
mScvhSupplier = scvhSupplier;
@@ -101,7 +126,10 @@
R.dimen.magnifier_border_width_fullscreen);
mDisplayId = mContext.getDisplayId();
mConfiguration = new Configuration(context.getResources().getConfiguration());
- mShowHideBorderAnimator = valueAnimator;
+ mLongAnimationTimeMs = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_longAnimTime);
+ mShowHideBorderAnimator = (valueAnimator == null)
+ ? createNullTargetObjectAnimator() : valueAnimator;
mShowHideBorderAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) {
@@ -114,15 +142,13 @@
});
}
- private static ValueAnimator createNullTargetObjectAnimator(Context context) {
+ private ValueAnimator createNullTargetObjectAnimator() {
final ValueAnimator valueAnimator =
ObjectAnimator.ofFloat(/* target= */ null, View.ALPHA, 0f, 1f);
Interpolator interpolator = new AccelerateDecelerateInterpolator();
- final long longAnimationDuration = context.getResources().getInteger(
- com.android.internal.R.integer.config_longAnimTime);
valueAnimator.setInterpolator(interpolator);
- valueAnimator.setDuration(longAnimationDuration);
+ valueAnimator.setDuration(mLongAnimationTimeMs);
return valueAnimator;
}
@@ -149,7 +175,11 @@
*/
@UiThread
private void removeFullscreenMagnificationBorder() {
+ if (mHandler.hasCallbacks(mShowBorderRunnable)) {
+ mHandler.removeCallbacks(mShowBorderRunnable);
+ }
mContext.unregisterComponentCallbacks(this);
+
mShowHideBorderAnimator.reverse();
}
@@ -161,6 +191,11 @@
if (mFullscreenBorder != null) {
mFullscreenBorder = null;
+ try {
+ mIWindowManager.removeRotationWatcher(mRotationWatcher);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to remove rotation watcher", e);
+ }
}
}
@@ -186,6 +221,11 @@
mSurfaceControlViewHost = mScvhSupplier.get();
mSurfaceControlViewHost.setView(mFullscreenBorder, getBorderLayoutParams());
mBorderSurfaceControl = mSurfaceControlViewHost.getSurfacePackage().getSurfaceControl();
+ try {
+ mIWindowManager.watchRotation(mRotationWatcher, Display.DEFAULT_DISPLAY);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to register rotation watcher", e);
+ }
}
mTransaction
@@ -256,11 +296,55 @@
reCreateWindow = true;
}
- if (mFullscreenBorder != null && reCreateWindow) {
+ if (mFullscreenBorder == null) {
+ return;
+ }
+
+ if (reCreateWindow) {
final int newWidth = mWindowBounds.width() + 2 * mBorderOffset;
final int newHeight = mWindowBounds.height() + 2 * mBorderOffset;
mSurfaceControlViewHost.relayout(newWidth, newHeight);
}
+
+ // Rotating from Landscape to ReverseLandscape will not trigger the config changes in
+ // CONFIG_SCREEN_SIZE and CONFIG_ORIENTATION. Therefore, we would like to check the device
+ // rotation separately.
+ // Since there's a possibility that {@link onConfigurationChanged} comes before
+ // {@link onRotationChanged}, we would like to handle screen rotation in either case that
+ // happens earlier.
+ int newRotation = RotationUtils.getRotation(mContext);
+ if (newRotation != mRotation) {
+ mRotation = newRotation;
+ handleScreenRotation();
+ }
+ }
+
+ private boolean isActivated() {
+ return mFullscreenBorder != null;
+ }
+
+ private void handleScreenRotation() {
+ if (!isActivated()) {
+ return;
+ }
+
+ if (mHandler.hasCallbacks(mShowBorderRunnable)) {
+ mHandler.removeCallbacks(mShowBorderRunnable);
+ }
+
+ // We hide the border immediately as early as possible to beat the redrawing of window
+ // in response to the orientation change so users won't see a weird shape border.
+ mHandler.postAtFrontOfQueue(() -> {
+ mFullscreenBorder.setAlpha(0f);
+ });
+
+ mHandler.postDelayed(mShowBorderRunnable, mLongAnimationTimeMs);
+ }
+
+ private void showBorderWithNullCheck() {
+ if (mShowHideBorderAnimator != null) {
+ mShowHideBorderAnimator.start();
+ }
}
private void updateDimensions() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
index 35c2024..e22a4e4 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
@@ -34,6 +34,7 @@
import android.os.Message;
import android.util.SparseArray;
import android.view.Display;
+import android.view.IWindowManager;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.WindowManager;
@@ -148,13 +149,19 @@
DisplayIdIndexSupplier<FullscreenMagnificationController> {
private final Context mContext;
+ private final Handler mHandler;
private final Executor mExecutor;
+ private final IWindowManager mIWindowManager;
- FullscreenMagnificationControllerSupplier(Context context, DisplayManager displayManager,
- Executor executor) {
+ FullscreenMagnificationControllerSupplier(Context context,
+ DisplayManager displayManager,
+ Handler handler,
+ Executor executor, IWindowManager iWindowManager) {
super(displayManager);
mContext = context;
+ mHandler = handler;
mExecutor = executor;
+ mIWindowManager = iWindowManager;
}
@Override
@@ -166,9 +173,11 @@
windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
return new FullscreenMagnificationController(
windowContext,
+ mHandler,
mExecutor,
windowContext.getSystemService(AccessibilityManager.class),
windowContext.getSystemService(WindowManager.class),
+ mIWindowManager,
scvhSupplier);
}
}
@@ -211,14 +220,16 @@
DisplayIdIndexSupplier<MagnificationSettingsController> mMagnificationSettingsSupplier;
@Inject
- public Magnification(Context context, @Main Handler mainHandler, @Main Executor executor,
+ public Magnification(Context context,
+ @Main Handler mainHandler, @Main Executor executor,
CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
SysUiState sysUiState, OverviewProxyService overviewProxyService,
SecureSettings secureSettings, DisplayTracker displayTracker,
- DisplayManager displayManager, AccessibilityLogger a11yLogger) {
+ DisplayManager displayManager, AccessibilityLogger a11yLogger,
+ IWindowManager iWindowManager) {
this(context, mainHandler.getLooper(), executor, commandQueue,
modeSwitchesController, sysUiState, overviewProxyService, secureSettings,
- displayTracker, displayManager, a11yLogger);
+ displayTracker, displayManager, a11yLogger, iWindowManager);
}
@VisibleForTesting
@@ -226,7 +237,8 @@
CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
SysUiState sysUiState, OverviewProxyService overviewProxyService,
SecureSettings secureSettings, DisplayTracker displayTracker,
- DisplayManager displayManager, AccessibilityLogger a11yLogger) {
+ DisplayManager displayManager, AccessibilityLogger a11yLogger,
+ IWindowManager iWindowManager) {
mContext = context;
mHandler = new Handler(looper) {
@Override
@@ -248,7 +260,7 @@
mHandler, mWindowMagnifierCallback,
displayManager, sysUiState, secureSettings);
mFullscreenMagnificationControllerSupplier = new FullscreenMagnificationControllerSupplier(
- context, displayManager, mExecutor);
+ context, displayManager, mHandler, mExecutor, iWindowManager);
mMagnificationSettingsSupplier = new SettingsSupplier(context,
mMagnificationSettingsControllerCallback, displayManager, secureSettings);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index 628b533..b4d53d0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -51,6 +51,7 @@
import com.android.systemui.biometrics.ui.viewmodel.isMedium
import com.android.systemui.biometrics.ui.viewmodel.isNullOrNotSmall
import com.android.systemui.biometrics.ui.viewmodel.isSmall
+import com.android.systemui.biometrics.ui.viewmodel.isTop
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import kotlin.math.abs
@@ -100,13 +101,13 @@
val iconHolderView = view.requireViewById<View>(R.id.biometric_icon)
val panelView = view.requireViewById<View>(R.id.panel)
val cornerRadius = view.resources.getDimension(R.dimen.biometric_dialog_corner_size)
- val cornerRadiusPx =
+ val pxToDp =
TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP,
- cornerRadius,
- view.resources.displayMetrics
- )
- .toInt()
+ TypedValue.COMPLEX_UNIT_DIP,
+ 1f,
+ view.resources.displayMetrics
+ )
+ val cornerRadiusPx = (pxToDp * cornerRadius).toInt()
var currentSize: PromptSize? = null
var currentPosition: PromptPosition = PromptPosition.Bottom
@@ -132,18 +133,10 @@
cornerRadiusPx.toFloat()
)
}
+ PromptPosition.Bottom,
PromptPosition.Top -> {
outline.setRoundRect(
0,
- -cornerRadiusPx,
- view.width,
- view.height,
- cornerRadiusPx.toFloat()
- )
- }
- PromptPosition.Bottom -> {
- outline.setRoundRect(
- 0,
0,
view.width,
view.height + cornerRadiusPx,
@@ -308,6 +301,7 @@
}
}
}
+
lifecycleScope.launch {
viewModel.iconSize.collect { iconSize ->
iconHolderView.layoutParams.width = iconSize.first
@@ -385,6 +379,7 @@
}
}
}
+
lifecycleScope.launch {
combine(viewModel.hideSensorIcon, viewModel.size, ::Pair).collect {
(hideSensorIcon, size) ->
@@ -415,6 +410,33 @@
R.id.rightGuideline,
ConstraintSet.RIGHT
)
+ } else if (position.isTop) {
+ // Top position is only used for 180 rotation Udfps
+ // Requires repositioning due to sensor location at top of screen
+ mediumConstraintSet.connect(
+ R.id.scrollView,
+ ConstraintSet.TOP,
+ R.id.indicator,
+ ConstraintSet.BOTTOM
+ )
+ mediumConstraintSet.connect(
+ R.id.scrollView,
+ ConstraintSet.BOTTOM,
+ R.id.button_bar,
+ ConstraintSet.TOP
+ )
+ mediumConstraintSet.connect(
+ R.id.panel,
+ ConstraintSet.TOP,
+ R.id.biometric_icon,
+ ConstraintSet.TOP
+ )
+ mediumConstraintSet.setMargin(
+ R.id.panel,
+ ConstraintSet.TOP,
+ (-24 * pxToDp).toInt()
+ )
+ mediumConstraintSet.setVerticalBias(R.id.scrollView, 0f)
}
when {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index d95a893..68a3f5d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -262,7 +262,8 @@
_forceLargeSize,
displayStateInteractor.isLargeScreen,
displayStateInteractor.currentRotation,
- ) { forceLarge, isLargeScreen, rotation ->
+ modalities
+ ) { forceLarge, isLargeScreen, rotation, modalities ->
when {
forceLarge ||
isLargeScreen ||
@@ -270,7 +271,8 @@
PromptPosition.Bottom
rotation == DisplayRotation.ROTATION_90 -> PromptPosition.Right
rotation == DisplayRotation.ROTATION_270 -> PromptPosition.Left
- rotation == DisplayRotation.ROTATION_180 -> PromptPosition.Top
+ rotation == DisplayRotation.ROTATION_180 && modalities.hasUdfps ->
+ PromptPosition.Top
else -> PromptPosition.Bottom
}
}
@@ -362,7 +364,14 @@
landscapeMediumBottomPadding
)
}
- PromptPosition.Top -> Rect()
+ PromptPosition.Top ->
+ if (size.isSmall) {
+ Rect(0, 0, 0, portraitSmallBottomPadding)
+ } else if (size.isMedium && modalities.hasUdfps) {
+ Rect(0, 0, 0, sensorBounds.bottom)
+ } else {
+ Rect(0, 0, 0, portraitMediumBottomPadding)
+ }
}
}
.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index 426f484..50477b1 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -70,8 +70,6 @@
private var shouldOpenWidgetPickerOnStart = false
- private var lockOnDestroy = false
-
private val addWidgetActivityLauncher: ActivityResultLauncher<Intent> =
registerForActivityResult(StartActivityForResult()) { result ->
when (result.resultCode) {
@@ -97,8 +95,7 @@
run { Log.w(TAG, "No AppWidgetProviderInfo found in result.") }
}
}
- }
- ?: run { Log.w(TAG, "No data in result.") }
+ } ?: run { Log.w(TAG, "No data in result.") }
}
else ->
Log.w(
@@ -160,9 +157,9 @@
// Wait for the current scene to be idle on communal.
communalViewModel.isIdleOnCommunal.first { it }
- // Then finish the activity (this helps to avoid a flash of lockscreen when locking
- // in onDestroy()).
- lockOnDestroy = true
+
+ // Lock to go back to the hub after exiting.
+ lockNow()
finish()
}
}
@@ -196,8 +193,6 @@
override fun onDestroy() {
super.onDestroy()
communalViewModel.setEditModeOpen(false)
-
- if (lockOnDestroy) lockNow()
}
private fun lockNow() {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/CommonSystemUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/CommonSystemUIUnfoldModule.kt
new file mode 100644
index 0000000..a91ce16
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/CommonSystemUIUnfoldModule.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dagger
+
+import com.android.systemui.unfold.SysUIUnfoldComponent
+import com.android.systemui.unfold.SysUIUnfoldModule.BoundFromSysUiUnfoldModule
+import dagger.BindsOptionalOf
+import dagger.Module
+import dagger.Provides
+import java.util.Optional
+import kotlin.jvm.optionals.getOrElse
+
+
+/**
+ * Module for foldable-related classes that is available in all SystemUI variants.
+ * Provides `Optional<SysUIUnfoldComponent>` which is present when the device is a foldable
+ * device that has fold/unfold animation enabled.
+ */
+@Module
+abstract class CommonSystemUIUnfoldModule {
+
+ /* Note this will be injected as @BoundFromSysUiUnfoldModule Optional<Optional<...>> */
+ @BindsOptionalOf
+ @BoundFromSysUiUnfoldModule
+ abstract fun optionalSysUiUnfoldComponent(): Optional<SysUIUnfoldComponent>
+
+ companion object {
+ @Provides
+ @SysUISingleton
+ fun sysUiUnfoldComponent(
+ /**
+ * This will be empty when [com.android.systemui.unfold.SysUIUnfoldModule] is not part
+ * of the graph, and contain the optional when it is.
+ */
+ @BoundFromSysUiUnfoldModule
+ optionalOfOptional: Optional<Optional<SysUIUnfoldComponent>>
+ ): Optional<SysUIUnfoldComponent> = optionalOfOptional.getOrElse { Optional.empty() }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
index a431a59..b71af69 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
@@ -19,6 +19,7 @@
import com.android.systemui.keyguard.CustomizationProvider;
import com.android.systemui.statusbar.NotificationInsetsModule;
import com.android.systemui.statusbar.QsFrameTranslateModule;
+import com.android.systemui.unfold.SysUIUnfoldModule;
import dagger.Subcomponent;
@@ -34,6 +35,7 @@
SystemUIBinder.class,
SystemUIModule.class,
SystemUICoreStartableModule.class,
+ SysUIUnfoldModule.class,
ReferenceSystemUIModule.class})
public interface ReferenceSysUIComponent extends SysUIComponent {
@@ -51,3 +53,4 @@
*/
void inject(CustomizationProvider customizationProvider);
}
+
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 2ebb94f..a7ff3c3 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -143,7 +143,6 @@
import com.android.systemui.telephony.data.repository.TelephonyRepositoryModule;
import com.android.systemui.temporarydisplay.dagger.TemporaryDisplayModule;
import com.android.systemui.tuner.dagger.TunerModule;
-import com.android.systemui.unfold.SysUIUnfoldModule;
import com.android.systemui.user.UserModule;
import com.android.systemui.user.domain.UserDomainLayerModule;
import com.android.systemui.util.EventLogModule;
@@ -254,7 +253,7 @@
SystemPropertiesFlagsModule.class,
SysUIConcurrencyModule.class,
SysUICoroutinesModule.class,
- SysUIUnfoldModule.class,
+ CommonSystemUIUnfoldModule.class,
TelephonyRepositoryModule.class,
TemporaryDisplayModule.class,
TunerModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
index 1b342ed..180afb2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
@@ -142,15 +142,17 @@
nonApps: Array<RemoteAnimationTarget>,
finishedCallback: IRemoteAnimationFinishedCallback
) {
- if (apps.isNotEmpty()) {
- // Ensure that we've started a dismiss keyguard transition. WindowManager can start the
- // going away animation on its own, if an activity launches and then requests dismissing
- // the keyguard. In this case, this is the first and only signal we'll receive to start
- // a transition to GONE.
- keyguardTransitionInteractor.startDismissKeyguardTransition(
- reason = "Going away remote animation started"
- )
+ // Ensure that we've started a dismiss keyguard transition. WindowManager can start the
+ // going away animation on its own, if an activity launches and then requests dismissing the
+ // keyguard. In this case, this is the first and only signal we'll receive to start
+ // a transition to GONE. This transition needs to start even if we're not provided an app
+ // animation target - it's possible the app is destroyed on creation, etc. but we'll still
+ // be unlocking.
+ keyguardTransitionInteractor.startDismissKeyguardTransition(
+ reason = "Going away remote animation started"
+ )
+ if (apps.isNotEmpty()) {
goingAwayRemoteAnimationFinishedCallback = finishedCallback
keyguardSurfaceBehindAnimator.applyParamsToSurface(apps[0])
} else {
@@ -183,25 +185,11 @@
/**
* Sets the lockscreen state WM-side by calling ATMS#setLockScreenShown.
*
- * [lockscreenShowing] defaults to true, since it's only ever null during the boot sequence,
- * when we haven't yet called ATMS#setLockScreenShown. Typically,
- * setWmLockscreenState(lockscreenShowing = true) is called early in the boot sequence, before
- * setWmLockscreenState(aodVisible = true), so we don't expect to need to use this default, but
- * if so, true should be the right choice.
+ * If [lockscreenShowing] is null, it means we don't know if the lockscreen is showing yet. This
+ * will be decided by the [KeyguardTransitionBootInteractor] shortly.
*/
private fun setWmLockscreenState(
- lockscreenShowing: Boolean =
- this.isLockscreenShowing
- ?: true.also {
- Log.d(
- TAG,
- "Using isLockscreenShowing=true default in setWmLockscreenState, " +
- "because setAodVisible was called before the first " +
- "setLockscreenShown call during boot. This is not typical, but is " +
- "theoretically possible. If you're investigating the lockscreen " +
- "showing unexpectedly, start here."
- )
- },
+ lockscreenShowing: Boolean? = this.isLockscreenShowing,
aodVisible: Boolean = this.isAodVisible
) {
Log.d(
@@ -211,10 +199,27 @@
"aodVisible=$aodVisible)."
)
+ if (lockscreenShowing == null) {
+ Log.d(
+ TAG,
+ "isAodVisible=$aodVisible, but lockscreenShowing=null. Waiting for" +
+ "non-null lockscreenShowing before calling ATMS#setLockScreenShown, which" +
+ "will happen once KeyguardTransitionBootInteractor starts the boot transition."
+ )
+ this.isAodVisible = aodVisible
+ return
+ }
+
if (this.isLockscreenShowing == lockscreenShowing && this.isAodVisible == aodVisible) {
return
}
+ Log.d(
+ TAG,
+ "ATMS#setLockScreenShown(" +
+ "isLockscreenShowing=$lockscreenShowing, " +
+ "aodVisible=$aodVisible)."
+ )
activityTaskManagerService.setLockScreenShown(lockscreenShowing, aodVisible)
this.isLockscreenShowing = lockscreenShowing
this.isAodVisible = aodVisible
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index 118ea16..14eb972 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -26,8 +26,11 @@
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
import com.android.wm.shell.animation.Interpolators
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -36,7 +39,6 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filter
@@ -46,7 +48,6 @@
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
-import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
@ExperimentalCoroutinesApi
@SysUISingleton
@@ -82,17 +83,16 @@
}
val surfaceBehindVisibility: Flow<Boolean?> =
- combine(
- transitionInteractor.startedKeyguardTransitionStep,
- transitionInteractor.transition(Edge.create(from = KeyguardState.ALTERNATE_BOUNCER))
- ) { startedStep, fromBouncerStep ->
- if (startedStep.to != KeyguardState.GONE) {
- return@combine null
- }
-
+ transitionInteractor
+ .transition(
+ edge = Edge.create(from = KeyguardState.ALTERNATE_BOUNCER, to = Scenes.Gone),
+ edgeWithoutSceneContainer =
+ Edge.create(from = KeyguardState.ALTERNATE_BOUNCER, to = KeyguardState.GONE)
+ )
+ .map<TransitionStep, Boolean?> {
// The alt bouncer is pretty fast to hide, so start the surface behind animation
// around 30%.
- fromBouncerStep.value > 0.3f
+ it.value > 0.3f
}
.onStart {
// Default to null ("don't care, use a reasonable default").
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 8cab3cd..f30eef0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -24,16 +24,18 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState.KEYGUARD
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
import java.util.UUID
@@ -59,7 +61,6 @@
@Background bgDispatcher: CoroutineDispatcher,
@Main mainDispatcher: CoroutineDispatcher,
keyguardInteractor: KeyguardInteractor,
- private val flags: FeatureFlags,
private val shadeRepository: ShadeRepository,
powerInteractor: PowerInteractor,
private val glanceableHubTransitions: GlanceableHubTransitions,
@@ -97,14 +98,13 @@
* LOCKSCREEN is running.
*/
val surfaceBehindVisibility: Flow<Boolean?> =
- transitionInteractor.startedKeyguardTransitionStep
- .map { startedStep ->
- if (startedStep.to != KeyguardState.GONE) {
- // LOCKSCREEN to anything but GONE does not require any special surface
- // visibility handling.
- return@map null
- }
-
+ transitionInteractor
+ .transition(
+ edge = Edge.create(from = KeyguardState.LOCKSCREEN, to = Scenes.Gone),
+ edgeWithoutSceneContainer =
+ Edge.create(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE)
+ )
+ .map<TransitionStep, Boolean?> {
true // Make the surface visible during LS -> GONE transitions.
}
.onStart {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index aaf935f..f8208b3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -22,15 +22,14 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
+import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.kotlin.Utils.Companion.sample
import com.android.systemui.util.kotlin.sample
@@ -40,8 +39,9 @@
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
@@ -56,7 +56,6 @@
@Main mainDispatcher: CoroutineDispatcher,
keyguardInteractor: KeyguardInteractor,
private val communalInteractor: CommunalInteractor,
- private val flags: FeatureFlags,
private val keyguardSecurityModel: KeyguardSecurityModel,
private val selectedUserInteractor: SelectedUserInteractor,
powerInteractor: PowerInteractor,
@@ -81,24 +80,25 @@
}
val surfaceBehindVisibility: Flow<Boolean?> =
- combine(
- transitionInteractor.startedKeyguardTransitionStep,
- transitionInteractor.transition(
- edge = Edge.create(from = Scenes.Bouncer),
- edgeWithoutSceneContainer = Edge.create(from = KeyguardState.PRIMARY_BOUNCER)
+ if (SceneContainerFlag.isEnabled) {
+ // The edge Scenes.Bouncer <-> Scenes.Gone is handled by STL
+ flowOf(null)
+ } else {
+ transitionInteractor
+ .transition(
+ edge = Edge.INVALID,
+ edgeWithoutSceneContainer =
+ Edge.create(from = KeyguardState.PRIMARY_BOUNCER, to = KeyguardState.GONE)
)
- ) { startedStep, fromBouncerStep ->
- if (startedStep.to != KeyguardState.GONE) {
- return@combine null
+ .map<TransitionStep, Boolean?> {
+ it.value > TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD
}
-
- fromBouncerStep.value > TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD
- }
- .onStart {
- // Default to null ("don't care, use a reasonable default").
- emit(null)
- }
- .distinctUntilChanged()
+ .onStart {
+ // Default to null ("don't care, use a reasonable default").
+ emit(null)
+ }
+ .distinctUntilChanged()
+ }
fun dismissPrimaryBouncer() {
scope.launch { startTransitionTo(KeyguardState.GONE) }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index 88e6602..3a43b1c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -229,11 +229,14 @@
val aodVisibility: Flow<Boolean> =
combine(
keyguardInteractor.isDozing,
+ keyguardInteractor.isAodAvailable,
keyguardInteractor.biometricUnlockState,
- ) { isDozing, biometricUnlockState ->
+ ) { isDozing, isAodAvailable, biometricUnlockState ->
// AOD is visible if we're dozing, unless we are wake and unlocking (where we go
// directly from AOD to unlocked while dozing).
- isDozing && !BiometricUnlockMode.isWakeAndUnlock(biometricUnlockState.mode)
+ isDozing &&
+ isAodAvailable &&
+ !BiometricUnlockMode.isWakeAndUnlock(biometricUnlockState.mode)
}
.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt
index 39f1ebe..aa0a9d9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt
@@ -62,7 +62,7 @@
addTransition(
clockViewModel.currentClock.value?.let { DefaultClockSteppingTransition(it) }
)
- else -> addTransition(ClockSizeTransition(config, clockViewModel, smartspaceViewModel))
+ else -> addTransition(ClockSizeTransition(config, clockViewModel))
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
index f17dbd2..4d914c7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
@@ -31,8 +31,9 @@
import com.android.app.animation.Interpolators
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Type
+import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.SmartspaceMoveTransition.Companion.STATUS_AREA_MOVE_DOWN_MILLIS
+import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.SmartspaceMoveTransition.Companion.STATUS_AREA_MOVE_UP_MILLIS
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.res.R
import com.android.systemui.shared.R as sharedR
import com.google.android.material.math.MathUtils
@@ -46,13 +47,12 @@
class ClockSizeTransition(
config: IntraBlueprintTransition.Config,
clockViewModel: KeyguardClockViewModel,
- smartspaceViewModel: KeyguardSmartspaceViewModel,
) : TransitionSet() {
init {
ordering = ORDERING_TOGETHER
if (config.type != Type.SmartspaceVisibility) {
- addTransition(ClockFaceOutTransition(config, clockViewModel, smartspaceViewModel))
- addTransition(ClockFaceInTransition(config, clockViewModel, smartspaceViewModel))
+ addTransition(ClockFaceOutTransition(config, clockViewModel))
+ addTransition(ClockFaceInTransition(config, clockViewModel))
}
addTransition(SmartspaceMoveTransition(config, clockViewModel))
}
@@ -60,8 +60,11 @@
abstract class VisibilityBoundsTransition() : Transition() {
abstract val captureSmartspace: Boolean
protected val TAG = this::class.simpleName!!
+
override fun captureEndValues(transition: TransitionValues) = captureValues(transition)
+
override fun captureStartValues(transition: TransitionValues) = captureValues(transition)
+
override fun getTransitionProperties(): Array<String> = TRANSITION_PROPERTIES
private fun captureValues(transition: TransitionValues) {
@@ -222,21 +225,19 @@
}
}
- class ClockFaceInTransition(
+ abstract class ClockFaceTransition(
config: IntraBlueprintTransition.Config,
val viewModel: KeyguardClockViewModel,
- val smartspaceViewModel: KeyguardSmartspaceViewModel,
) : VisibilityBoundsTransition() {
- override val captureSmartspace = !viewModel.isLargeClockVisible.value
+ protected abstract val isLargeClock: Boolean
+ protected abstract val smallClockMoveScale: Float
+ override val captureSmartspace
+ get() = !isLargeClock
- init {
- duration = CLOCK_IN_MILLIS
- startDelay = CLOCK_IN_START_DELAY_MILLIS
- interpolator = CLOCK_IN_INTERPOLATOR
-
- if (viewModel.isLargeClockVisible.value) {
+ protected fun addTargets() {
+ if (isLargeClock) {
viewModel.currentClock.value?.let {
- if (DEBUG) Log.i(TAG, "Large Clock In: ${it.largeClock.layout.views}")
+ if (DEBUG) Log.i(TAG, "Adding large clock views: ${it.largeClock.layout.views}")
it.largeClock.layout.views.forEach { addTarget(it) }
}
?: run {
@@ -244,7 +245,7 @@
addTarget(R.id.lockscreen_clock_view_large)
}
} else {
- if (DEBUG) Log.i(TAG, "Small Clock In")
+ if (DEBUG) Log.i(TAG, "Adding small clock")
addTarget(R.id.lockscreen_clock_view)
}
}
@@ -262,89 +263,59 @@
if (fromIsVis == toIsVis) return
fromBounds.set(toBounds)
- if (viewModel.isLargeClockVisible.value) {
+ if (isLargeClock) {
// Large clock shouldn't move; fromBounds already set
} else if (toSSBounds != null && fromSSBounds != null) {
// Instead of moving the small clock the full distance, we compute the distance
// smartspace will move. We then scale this to match the duration of this animation
// so that the small clock moves at the same speed as smartspace.
val ssTranslation =
- abs((toSSBounds.top - fromSSBounds.top) * SMALL_CLOCK_IN_MOVE_SCALE).toInt()
+ abs((toSSBounds.top - fromSSBounds.top) * smallClockMoveScale).toInt()
fromBounds.top = toBounds.top - ssTranslation
fromBounds.bottom = toBounds.bottom - ssTranslation
} else {
Log.e(TAG, "mutateBounds: smallClock received no smartspace bounds")
}
}
+ }
+
+ class ClockFaceInTransition(
+ config: IntraBlueprintTransition.Config,
+ viewModel: KeyguardClockViewModel,
+ ) : ClockFaceTransition(config, viewModel) {
+ override val isLargeClock = viewModel.isLargeClockVisible.value
+ override val smallClockMoveScale = CLOCK_IN_MILLIS / STATUS_AREA_MOVE_DOWN_MILLIS.toFloat()
+
+ init {
+ duration = CLOCK_IN_MILLIS
+ startDelay = CLOCK_IN_START_DELAY_MILLIS
+ interpolator = CLOCK_IN_INTERPOLATOR
+ addTargets()
+ }
companion object {
const val CLOCK_IN_MILLIS = 167L
const val CLOCK_IN_START_DELAY_MILLIS = 133L
val CLOCK_IN_INTERPOLATOR = Interpolators.LINEAR_OUT_SLOW_IN
- const val SMALL_CLOCK_IN_MOVE_SCALE =
- CLOCK_IN_MILLIS / SmartspaceMoveTransition.STATUS_AREA_MOVE_DOWN_MILLIS.toFloat()
}
}
class ClockFaceOutTransition(
config: IntraBlueprintTransition.Config,
- val viewModel: KeyguardClockViewModel,
- val smartspaceViewModel: KeyguardSmartspaceViewModel,
- ) : VisibilityBoundsTransition() {
- override val captureSmartspace = viewModel.isLargeClockVisible.value
+ viewModel: KeyguardClockViewModel,
+ ) : ClockFaceTransition(config, viewModel) {
+ override val isLargeClock = !viewModel.isLargeClockVisible.value
+ override val smallClockMoveScale = CLOCK_OUT_MILLIS / STATUS_AREA_MOVE_UP_MILLIS.toFloat()
init {
duration = CLOCK_OUT_MILLIS
interpolator = CLOCK_OUT_INTERPOLATOR
-
- if (viewModel.isLargeClockVisible.value) {
- if (DEBUG) Log.i(TAG, "Small Clock Out")
- addTarget(R.id.lockscreen_clock_view)
- } else {
- viewModel.currentClock.value?.let {
- if (DEBUG) Log.i(TAG, "Large Clock Out: ${it.largeClock.layout.views}")
- it.largeClock.layout.views.forEach { addTarget(it) }
- }
- ?: run {
- Log.e(TAG, "No large clock set, falling back")
- addTarget(R.id.lockscreen_clock_view_large)
- }
- }
- }
-
- override fun mutateBounds(
- view: View,
- fromIsVis: Boolean,
- toIsVis: Boolean,
- fromBounds: Rect,
- toBounds: Rect,
- fromSSBounds: Rect?,
- toSSBounds: Rect?
- ) {
- // Move normally if clock is not changing visibility
- if (fromIsVis == toIsVis) return
-
- toBounds.set(fromBounds)
- if (!viewModel.isLargeClockVisible.value) {
- // Large clock shouldn't move; toBounds already set
- } else if (toSSBounds != null && fromSSBounds != null) {
- // Instead of moving the small clock the full distance, we compute the distance
- // smartspace will move. We then scale this to match the duration of this animation
- // so that the small clock moves at the same speed as smartspace.
- val ssTranslation =
- abs((toSSBounds.top - fromSSBounds.top) * SMALL_CLOCK_OUT_MOVE_SCALE).toInt()
- toBounds.top = fromBounds.top - ssTranslation
- toBounds.bottom = fromBounds.bottom - ssTranslation
- } else {
- Log.e(TAG, "mutateBounds: smallClock received no smartspace bounds")
- }
+ addTargets()
}
companion object {
const val CLOCK_OUT_MILLIS = 133L
val CLOCK_OUT_INTERPOLATOR = Interpolators.LINEAR
- const val SMALL_CLOCK_OUT_MOVE_SCALE =
- CLOCK_OUT_MILLIS / SmartspaceMoveTransition.STATUS_AREA_MOVE_UP_MILLIS.toFloat()
}
}
@@ -353,15 +324,14 @@
val config: IntraBlueprintTransition.Config,
viewModel: KeyguardClockViewModel,
) : VisibilityBoundsTransition() {
+ private val isLargeClock = viewModel.isLargeClockVisible.value
override val captureSmartspace = false
init {
duration =
- if (viewModel.isLargeClockVisible.value) STATUS_AREA_MOVE_UP_MILLIS
- else STATUS_AREA_MOVE_DOWN_MILLIS
+ if (isLargeClock) STATUS_AREA_MOVE_UP_MILLIS else STATUS_AREA_MOVE_DOWN_MILLIS
interpolator = Interpolators.EMPHASIZED
addTarget(sharedR.id.date_smartspace_view)
- addTarget(sharedR.id.weather_smartspace_view)
addTarget(sharedR.id.bc_smartspace_view)
// Notifications normally and media on split shade needs to be moved
@@ -391,6 +361,6 @@
}
companion object {
- val DEBUG = true
+ val DEBUG = false
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
index 0f1f5c1..06b76b3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
@@ -52,8 +52,11 @@
udfpsOverlayInteractor: UdfpsOverlayInteractor,
) {
private val isShowingAodOrDozing: Flow<Boolean> =
- transitionInteractor.startedKeyguardState.map { keyguardState ->
- keyguardState == KeyguardState.AOD || keyguardState == KeyguardState.DOZING
+ combine(
+ transitionInteractor.startedKeyguardState,
+ transitionInteractor.transitionValue(KeyguardState.DOZING),
+ ) { startedKeyguardState, dozingTransitionValue ->
+ startedKeyguardState == KeyguardState.AOD || dozingTransitionValue == 1f
}
private fun getColor(usingBackgroundProtection: Boolean): Int {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index fa43ec2..92bba38 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -264,7 +264,7 @@
accessibilityInteractor.isEnabled.flatMapLatest { touchExplorationEnabled ->
if (touchExplorationEnabled) {
combine(iconType, isInteractive) { iconType, isInteractive ->
- if (isInteractive) {
+ if (isInteractive || iconType == DeviceEntryIconView.IconType.LOCK) {
iconType.toAccessibilityHintType()
} else {
DeviceEntryIconView.AccessibilityHintType.NONE
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt
index 7ac03bf..c6efcfa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt
@@ -130,6 +130,6 @@
companion object {
private const val TAG = "KeyguardBlueprintViewModel"
- private const val DEBUG = true
+ private const val DEBUG = false
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
index 0c70f10..220d326 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
@@ -218,9 +218,9 @@
mediaFromRecPackageName = null
_currentMedia.value = sortedMap.values.toList()
}
- } else if (sortedMap.size > sortedMedia.size) {
+ } else if (sortedMap.size > sortedMedia.size && it.active) {
_currentMedia.value = sortedMap.values.toList()
- } else if (sortedMap.size == sortedMedia.size) {
+ } else {
// When loading an update for an existing media control.
val currentList =
mutableListOf<MediaCommonModel>().apply { addAll(_currentMedia.value) }
@@ -296,6 +296,18 @@
mediaFromRecPackageName = packageName
}
+ fun hasActiveMedia(): Boolean {
+ return _selectedUserEntries.value.any { it.value.active }
+ }
+
+ fun hasAnyMedia(): Boolean {
+ return _selectedUserEntries.value.entries.isNotEmpty()
+ }
+
+ fun isRecommendationActive(): Boolean {
+ return _smartspaceMediaData.value.isActive
+ }
+
private fun canBeRemoved(data: MediaData): Boolean {
return data.isPlaying?.let { !it } ?: data.isClearable && !data.active
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
index aa93df7..8b2f619 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
@@ -358,7 +358,12 @@
// LocalMediaManager. Override with routing session name if available to
// show dynamic group name.
connectedDevice?.copy(name = it.name ?: connectedDevice.name)
- }
+ } ?: MediaDeviceData(
+ enabled = false,
+ icon = context.getDrawable(R.drawable.ic_media_home_devices),
+ name = context.getString(R.string.media_seamless_other_device),
+ showBroadcastButton = false
+ )
} else {
// Prefer SASS if available when playback is local.
activeDevice = getSassDevice() ?: connectedDevice
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
index b4bd4fd..0630cbd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
@@ -25,7 +25,6 @@
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.media.controls.data.repository.MediaDataRepository
import com.android.systemui.media.controls.data.repository.MediaFilterRepository
import com.android.systemui.media.controls.domain.pipeline.MediaDataCombineLatest
import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl
@@ -45,7 +44,6 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.stateIn
/** Encapsulates business logic for media pipeline. */
@@ -55,7 +53,6 @@
@Inject
constructor(
@Application applicationScope: CoroutineScope,
- private val mediaDataRepository: MediaDataRepository,
private val mediaDataProcessor: MediaDataProcessor,
private val mediaTimeoutListener: MediaTimeoutListener,
private val mediaResumeListener: MediaResumeListener,
@@ -103,26 +100,6 @@
initialValue = false,
)
- /** Are there any media notifications active, excluding the recommendations? */
- val hasActiveMedia: StateFlow<Boolean> =
- mediaFilterRepository.selectedUserEntries
- .mapLatest { entries -> entries.any { it.value.active } }
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = false,
- )
-
- /** Are there any media notifications, excluding the recommendations? */
- val hasAnyMedia: StateFlow<Boolean> =
- mediaFilterRepository.selectedUserEntries
- .mapLatest { entries -> entries.isNotEmpty() }
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = false,
- )
-
/** The current list for user media instances */
val currentMedia: StateFlow<List<MediaCommonModel>> = mediaFilterRepository.currentMedia
@@ -235,11 +212,11 @@
override fun hasAnyMediaOrRecommendation() = hasAnyMediaOrRecommendation.value
- override fun hasActiveMedia() = hasActiveMedia.value
+ override fun hasActiveMedia() = mediaFilterRepository.hasActiveMedia()
- override fun hasAnyMedia() = hasAnyMedia.value
+ override fun hasAnyMedia() = mediaFilterRepository.hasAnyMedia()
- override fun isRecommendationActive() = mediaDataRepository.smartspaceMediaData.value.isActive
+ override fun isRecommendationActive() = mediaFilterRepository.isRecommendationActive()
fun reorderMedia() {
mediaFilterRepository.setOrderedMedia()
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index 8f15561..987b370 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -216,7 +216,7 @@
private var carouselLocale: Locale? = null
private val animationScaleObserver: ContentObserver =
- object : ContentObserver(null) {
+ object : ContentObserver(executor, 0) {
override fun onChange(selfChange: Boolean) {
if (!mediaFlags.isSceneContainerEnabled()) {
MediaPlayerData.players().forEach { it.updateAnimatorDurationScale() }
@@ -396,10 +396,12 @@
}
// Notifies all active players about animation scale changes.
- globalSettings.registerContentObserverSync(
- Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
- animationScaleObserver
- )
+ bgExecutor.execute {
+ globalSettings.registerContentObserverSync(
+ Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
+ animationScaleObserver
+ )
+ }
}
private fun setUpListeners() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
index 4e90936..315a9fb 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
@@ -201,6 +201,7 @@
) {
if (immediatelyRemove || isReorderingAllowed()) {
interactor.dismissSmartspaceRecommendation(commonModel.recsLoadingModel.key, 0L)
+ mediaRecs = null
if (!immediatelyRemove) {
// Although it wasn't requested, we were able to process the removal
// immediately since reordering is allowed. So, notify hosts to update
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
index 03adf1b..01b1be9 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
@@ -58,7 +58,7 @@
// opening the app selector in split screen mode, the foreground task will be the second
// task in index 0.
val foregroundGroup =
- if (groupedTasks.first().splitBounds != null) groupedTasks.first()
+ if (groupedTasks.firstOrNull()?.splitBounds != null) groupedTasks.first()
else groupedTasks.elementAtOrNull(1)
val foregroundTaskId1 = foregroundGroup?.taskInfo1?.taskId
val foregroundTaskId2 = foregroundGroup?.taskInfo2?.taskId
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index e861ddf..f004c3a 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -23,6 +23,7 @@
import static android.media.projection.MediaProjectionManager.OVERRIDE_DISABLE_MEDIA_PROJECTION_SINGLE_APP_OPTION;
import static android.media.projection.ReviewGrantedConsentResult.RECORD_CANCEL;
import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY;
+import static android.os.UserHandle.USER_SYSTEM;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static com.android.systemui.mediaprojection.permission.ScreenShareOptionKt.ENTIRE_SCREEN;
@@ -31,7 +32,6 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.app.Activity;
-import android.app.ActivityManager;
import android.app.ActivityOptions.LaunchCookie;
import android.app.AlertDialog;
import android.app.StatusBarManager;
@@ -366,11 +366,11 @@
intent.putExtra(EXTRA_USER_REVIEW_GRANTED_CONSENT, mReviewGrantedConsentRequired);
intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
- // Start activity from the current foreground user to avoid creating a separate
- // SystemUI process without access to recent tasks because it won't have
- // WM Shell running inside.
+ // Start activity as system user and manually show app selector to all users to
+ // avoid creating a separate SystemUI process without access to recent tasks
+ // because it won't have WM Shell running inside.
mUserSelectingTask = true;
- startActivityAsUser(intent, UserHandle.of(ActivityManager.getCurrentUser()));
+ startActivityAsUser(intent, UserHandle.of(USER_SYSTEM));
// close shade if it's open
mStatusBarManager.collapsePanels();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index f3cc35ba..abc2b7f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -217,10 +217,13 @@
private void setBrightnessViewMargin() {
if (mBrightnessView != null) {
MarginLayoutParams lp = (MarginLayoutParams) mBrightnessView.getLayoutParams();
+ // For Brightness Slider to extend its boundary to draw focus background
+ int offset = getResources()
+ .getDimensionPixelSize(R.dimen.rounded_slider_boundary_offset);
lp.topMargin = mContext.getResources()
- .getDimensionPixelSize(R.dimen.qs_brightness_margin_top);
+ .getDimensionPixelSize(R.dimen.qs_brightness_margin_top) - offset;
lp.bottomMargin = mContext.getResources()
- .getDimensionPixelSize(R.dimen.qs_brightness_margin_bottom);
+ .getDimensionPixelSize(R.dimen.qs_brightness_margin_bottom) - offset;
mBrightnessView.setLayoutParams(lp);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt
index e1b21ef..9233e76 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt
@@ -16,7 +16,6 @@
package com.android.systemui.qs.panels.ui.compose
-import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -40,6 +39,13 @@
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawWithContent
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.PathEffect
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.addOutline
+import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp
@@ -268,10 +274,9 @@
private fun CurrentTilesContainer(content: @Composable () -> Unit) {
Box(
Modifier.fillMaxWidth()
- .border(
- width = 1.dp,
- color = MaterialTheme.colorScheme.onBackground,
- shape = RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius))
+ .dashedBorder(
+ color = MaterialTheme.colorScheme.onBackground.copy(alpha = .5f),
+ shape = Dimensions.ContainerShape,
)
.padding(dimensionResource(R.dimen.qs_tile_margin_vertical))
) {
@@ -286,7 +291,7 @@
.background(
color = MaterialTheme.colorScheme.background,
alpha = { 1f },
- shape = RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius))
+ shape = Dimensions.ContainerShape,
)
.padding(dimensionResource(R.dimen.qs_tile_margin_vertical))
) {
@@ -305,4 +310,27 @@
item(span = { GridItemSpan(maxCurrentLineSpan) }) { Spacer(Modifier) }
}
}
+
+ private fun Modifier.dashedBorder(
+ color: Color,
+ shape: Shape,
+ ): Modifier {
+ return this.drawWithContent {
+ val outline = shape.createOutline(size, layoutDirection, this)
+ val path = Path()
+ path.addOutline(outline)
+ val stroke =
+ Stroke(
+ width = 1.dp.toPx(),
+ pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f))
+ )
+ this.drawContent()
+ drawPath(path = path, style = stroke, color = color)
+ }
+ }
+
+ private object Dimensions {
+ // Corner radius is half the height of a tile + padding
+ val ContainerShape = RoundedCornerShape(48.dp)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
index e4fbb4b..bbb98d3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalFoundationApi::class)
+
package com.android.systemui.qs.panels.ui.compose
import android.graphics.drawable.Animatable
@@ -27,17 +29,17 @@
import androidx.compose.animation.graphics.vector.AnimatedImageVector
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
-import androidx.compose.foundation.background
import androidx.compose.foundation.basicMarquee
-import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
@@ -46,11 +48,6 @@
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Add
-import androidx.compose.material.icons.filled.Remove
-import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -68,7 +65,6 @@
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.onClick
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.stateDescription
@@ -78,9 +74,11 @@
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.Expandable
+import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.common.ui.compose.load
+import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.panels.ui.viewmodel.AvailableEditActions
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileUiState
@@ -90,13 +88,14 @@
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.res.R
+import java.util.function.Supplier
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.mapLatest
object TileType
-@OptIn(ExperimentalCoroutinesApi::class, ExperimentalFoundationApi::class)
+@OptIn(ExperimentalCoroutinesApi::class)
@Composable
fun Tile(
tile: TileViewModel,
@@ -110,46 +109,143 @@
.collectAsStateWithLifecycle(tile.currentState.toUiState())
val colors = TileDefaults.getColorForState(state.state)
- val context = LocalContext.current
-
- Expandable(
- color = colors.background,
- shape = RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius)),
+ TileContainer(
+ colors = colors,
+ showLabels = showLabels,
+ label = state.label.toString(),
+ iconOnly = iconOnly,
+ onClick = tile::onClick,
+ onLongClick = tile::onLongClick,
+ modifier = modifier,
) {
- Row(
- modifier =
- modifier
- .combinedClickable(
- onClick = { tile.onClick(it) },
- onLongClick = { tile.onLongClick(it) }
- )
- .tileModifier(colors),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = tileHorizontalArrangement(iconOnly),
- ) {
- val icon =
- remember(state.icon) {
- state.icon.get().let {
- if (it is QSTileImpl.ResourceIcon) {
- Icon.Resource(it.resId, null)
- } else {
- Icon.Loaded(it.getDrawable(context), null)
- }
- }
- }
- TileContent(
+ val icon = getTileIcon(icon = state.icon)
+ if (iconOnly) {
+ TileIcon(icon = icon, color = colors.icon, modifier = Modifier.align(Alignment.Center))
+ } else {
+ LargeTileContent(
label = state.label.toString(),
secondaryLabel = state.secondaryLabel.toString(),
icon = icon,
colors = colors,
- iconOnly = iconOnly,
- showLabels = showLabels,
+ onClick = tile::onSecondaryClick,
+ onLongClick = tile::onLongClick,
)
}
}
}
@Composable
+private fun TileContainer(
+ colors: TileColors,
+ showLabels: Boolean,
+ label: String,
+ iconOnly: Boolean,
+ clickEnabled: Boolean = true,
+ onClick: (Expandable) -> Unit = {},
+ onLongClick: (Expandable) -> Unit = {},
+ modifier: Modifier = Modifier,
+ content: @Composable BoxScope.() -> Unit,
+) {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement =
+ spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin), Alignment.Top),
+ modifier = modifier,
+ ) {
+ val backgroundColor =
+ if (iconOnly) {
+ colors.iconBackground
+ } else {
+ colors.background
+ }
+ Expandable(
+ color = backgroundColor,
+ shape = TileDefaults.TileShape,
+ modifier =
+ Modifier.height(dimensionResource(id = R.dimen.qs_tile_height))
+ .clip(TileDefaults.TileShape)
+ ) {
+ Box(
+ modifier =
+ Modifier.fillMaxSize()
+ .combinedClickable(
+ enabled = clickEnabled,
+ onClick = { onClick(it) },
+ onLongClick = { onLongClick(it) }
+ )
+ .tilePadding(),
+ ) {
+ content()
+ }
+ }
+
+ if (showLabels && iconOnly) {
+ Text(
+ label,
+ maxLines = 2,
+ color = colors.label,
+ overflow = TextOverflow.Ellipsis,
+ textAlign = TextAlign.Center,
+ )
+ }
+ }
+}
+
+@Composable
+private fun LargeTileContent(
+ label: String,
+ secondaryLabel: String?,
+ icon: Icon,
+ colors: TileColors,
+ clickEnabled: Boolean = true,
+ onClick: (Expandable) -> Unit = {},
+ onLongClick: (Expandable) -> Unit = {},
+) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = tileHorizontalArrangement()
+ ) {
+ Expandable(
+ color = colors.iconBackground,
+ shape = TileDefaults.TileShape,
+ modifier = Modifier.fillMaxHeight().aspectRatio(1f)
+ ) {
+ Box(
+ modifier =
+ Modifier.fillMaxSize()
+ .clip(TileDefaults.TileShape)
+ .combinedClickable(
+ enabled = clickEnabled,
+ onClick = { onClick(it) },
+ onLongClick = { onLongClick(it) }
+ )
+ ) {
+ TileIcon(
+ icon = icon,
+ color = colors.icon,
+ modifier = Modifier.align(Alignment.Center)
+ )
+ }
+ }
+
+ Column(verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxHeight()) {
+ Text(
+ label,
+ color = colors.label,
+ modifier = Modifier.basicMarquee(),
+ )
+ if (!TextUtils.isEmpty(secondaryLabel)) {
+ Text(
+ secondaryLabel ?: "",
+ color = colors.secondaryLabel,
+ modifier = Modifier.basicMarquee(),
+ )
+ }
+ }
+ }
+}
+
+@Composable
fun TileLazyGrid(
modifier: Modifier = Modifier,
columns: GridCells,
@@ -247,41 +343,19 @@
""
}
- Box(
+ val iconOnly = isIconOnly(viewModel.tileSpec)
+ val tileHeight = tileHeight(iconOnly && showLabels)
+ EditTile(
+ tileViewModel = viewModel,
+ iconOnly = iconOnly,
+ showLabels = showLabels,
+ clickEnabled = canClick,
+ onClick = { onClick.invoke(viewModel.tileSpec) },
modifier =
- Modifier.clickable(enabled = canClick) { onClick.invoke(viewModel.tileSpec) }
- .animateItem()
- .semantics {
- onClick(onClickActionName) { false }
- this.stateDescription = stateDescription
- }
- ) {
- val iconOnly = isIconOnly(viewModel.tileSpec)
- val tileHeight = tileHeight(iconOnly && showLabels)
- EditTile(
- tileViewModel = viewModel,
- iconOnly = iconOnly,
- showLabels = showLabels,
- modifier = Modifier.height(tileHeight)
- )
- if (canClick) {
- Badge(clickAction, Modifier.align(Alignment.TopEnd))
- }
- }
- }
-}
-
-@Composable
-fun Badge(action: ClickAction, modifier: Modifier = Modifier) {
- Box(modifier = modifier.size(16.dp).background(Color.Cyan, shape = CircleShape)) {
- Icon(
- imageVector =
- when (action) {
- ClickAction.ADD -> Icons.Filled.Add
- ClickAction.REMOVE -> Icons.Filled.Remove
- },
- "",
- tint = Color.Black,
+ Modifier.height(tileHeight).animateItem().semantics {
+ onClick(onClickActionName) { false }
+ this.stateDescription = stateDescription
+ }
)
}
}
@@ -291,25 +365,40 @@
tileViewModel: EditTileViewModel,
iconOnly: Boolean,
showLabels: Boolean,
+ clickEnabled: Boolean,
+ onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
val label = tileViewModel.label.load() ?: tileViewModel.tileSpec.spec
val colors = TileDefaults.inactiveTileColors()
- Row(
- modifier = modifier.tileModifier(colors).semantics { this.contentDescription = label },
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = tileHorizontalArrangement(iconOnly)
+ TileContainer(
+ colors = colors,
+ showLabels = showLabels,
+ label = label,
+ iconOnly = iconOnly,
+ clickEnabled = clickEnabled,
+ onClick = { onClick() },
+ onLongClick = { onClick() },
+ modifier = modifier,
) {
- TileContent(
- label = label,
- secondaryLabel = tileViewModel.appName?.load(),
- colors = colors,
- icon = tileViewModel.icon,
- iconOnly = iconOnly,
- showLabels = showLabels,
- animateIconToEnd = true,
- )
+ if (iconOnly) {
+ TileIcon(
+ icon = tileViewModel.icon,
+ color = colors.icon,
+ modifier = Modifier.align(Alignment.Center)
+ )
+ } else {
+ LargeTileContent(
+ label = label,
+ secondaryLabel = tileViewModel.appName?.load(),
+ icon = tileViewModel.icon,
+ colors = colors,
+ clickEnabled = clickEnabled,
+ onClick = { onClick() },
+ onLongClick = { onClick() },
+ )
+ }
}
}
@@ -318,14 +407,27 @@
REMOVE,
}
+@Composable
+private fun getTileIcon(icon: Supplier<QSTile.Icon>): Icon {
+ val context = LocalContext.current
+ return icon.get().let {
+ if (it is QSTileImpl.ResourceIcon) {
+ Icon.Resource(it.resId, null)
+ } else {
+ Icon.Loaded(it.getDrawable(context), null)
+ }
+ }
+}
+
@OptIn(ExperimentalAnimationGraphicsApi::class)
@Composable
private fun TileIcon(
icon: Icon,
color: Color,
animateToEnd: Boolean = false,
+ modifier: Modifier = Modifier,
) {
- val modifier = Modifier.size(dimensionResource(id = R.dimen.qs_icon_size))
+ val iconModifier = modifier.size(dimensionResource(id = R.dimen.qs_icon_size))
val context = LocalContext.current
val loadedDrawable =
remember(icon, context) {
@@ -338,7 +440,7 @@
Icon(
icon = icon,
tint = color,
- modifier = modifier,
+ modifier = iconModifier,
)
} else if (icon is Icon.Resource) {
val image = AnimatedImageVector.animatedVectorResource(id = icon.res)
@@ -357,80 +459,25 @@
painter = painter,
contentDescription = null,
colorFilter = ColorFilter.tint(color = color),
- modifier = modifier
+ modifier = iconModifier
)
}
}
@Composable
-private fun Modifier.tileModifier(colors: TileColors): Modifier {
- return fillMaxWidth()
- .clip(RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius)))
- .background(colors.background)
- .padding(horizontal = dimensionResource(id = R.dimen.qs_label_container_margin))
+private fun Modifier.tilePadding(): Modifier {
+ return padding(dimensionResource(id = R.dimen.qs_label_container_margin))
}
@Composable
-private fun tileHorizontalArrangement(iconOnly: Boolean): Arrangement.Horizontal {
- val horizontalAlignment =
- if (iconOnly) {
- Alignment.CenterHorizontally
- } else {
- Alignment.Start
- }
+private fun tileHorizontalArrangement(): Arrangement.Horizontal {
return spacedBy(
space = dimensionResource(id = R.dimen.qs_label_container_margin),
- alignment = horizontalAlignment
+ alignment = Alignment.Start
)
}
@Composable
-private fun TileContent(
- label: String,
- secondaryLabel: String?,
- icon: Icon,
- colors: TileColors,
- iconOnly: Boolean,
- showLabels: Boolean = false,
- animateIconToEnd: Boolean = false,
-) {
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.Center,
- modifier = Modifier.fillMaxHeight()
- ) {
- TileIcon(icon, colors.icon, animateIconToEnd)
-
- if (iconOnly && showLabels) {
- Text(
- label,
- maxLines = 2,
- color = colors.label,
- overflow = TextOverflow.Ellipsis,
- textAlign = TextAlign.Center,
- )
- }
- }
-
- if (!iconOnly) {
- Column(verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxHeight()) {
- Text(
- label,
- color = colors.label,
- modifier = Modifier.basicMarquee(),
- )
- if (!TextUtils.isEmpty(secondaryLabel)) {
- Text(
- secondaryLabel ?: "",
- color = colors.secondaryLabel,
- modifier = Modifier.basicMarquee(),
- )
- }
- }
- }
-}
-
-@Composable
fun tileHeight(iconWithLabel: Boolean = false): Dp {
return if (iconWithLabel) {
TileDefaults.IconTileWithLabelHeight
@@ -441,20 +488,23 @@
private data class TileColors(
val background: Color,
+ val iconBackground: Color,
val label: Color,
val secondaryLabel: Color,
val icon: Color,
)
private object TileDefaults {
- val IconTileWithLabelHeight = 100.dp
+ val TileShape = CircleShape
+ val IconTileWithLabelHeight = 140.dp
@Composable
fun activeTileColors(): TileColors =
TileColors(
- background = MaterialTheme.colorScheme.primary,
- label = MaterialTheme.colorScheme.onPrimary,
- secondaryLabel = MaterialTheme.colorScheme.onPrimary,
+ background = MaterialTheme.colorScheme.surfaceVariant,
+ iconBackground = MaterialTheme.colorScheme.primary,
+ label = MaterialTheme.colorScheme.onSurfaceVariant,
+ secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
icon = MaterialTheme.colorScheme.onPrimary,
)
@@ -462,6 +512,7 @@
fun inactiveTileColors(): TileColors =
TileColors(
background = MaterialTheme.colorScheme.surfaceVariant,
+ iconBackground = MaterialTheme.colorScheme.surfaceVariant,
label = MaterialTheme.colorScheme.onSurfaceVariant,
secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
icon = MaterialTheme.colorScheme.onSurfaceVariant,
@@ -471,6 +522,7 @@
fun unavailableTileColors(): TileColors =
TileColors(
background = MaterialTheme.colorScheme.surface,
+ iconBackground = MaterialTheme.colorScheme.surface,
label = MaterialTheme.colorScheme.onSurface,
secondaryLabel = MaterialTheme.colorScheme.onSurface,
icon = MaterialTheme.colorScheme.onSurface,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt
index a6cfa75..7505b90 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt
@@ -48,6 +48,10 @@
tile.longClick(expandable)
}
+ fun onSecondaryClick(expandable: Expandable?) {
+ tile.secondaryClick(expandable)
+ }
+
fun startListening(token: Any) = tile.setListening(token, true)
fun stopListening(token: Any) = tile.setListening(token, false)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt
index d38e834..1d08f2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt
@@ -25,6 +25,9 @@
* given mobile data subscription.
*/
interface DeviceBasedSatelliteRepository {
+ /** The current status of satellite provisioning. If not false, we don't want to show an icon */
+ val isSatelliteProvisioned: StateFlow<Boolean>
+
/** See [SatelliteConnectionState] for available states */
val connectionState: StateFlow<SatelliteConnectionState>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt
index 6b1bc65..58c30e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt
@@ -97,6 +97,11 @@
}
.stateIn(scope, SharingStarted.WhileSubscribed(), realImpl)
+ override val isSatelliteProvisioned: StateFlow<Boolean> =
+ activeRepo
+ .flatMapLatest { it.isSatelliteProvisioned }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl.isSatelliteProvisioned.value)
+
override val connectionState: StateFlow<SatelliteConnectionState> =
activeRepo
.flatMapLatest { it.connectionState }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt
index 56034f0..6ad295e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt
@@ -36,6 +36,7 @@
) : DeviceBasedSatelliteRepository {
private var demoCommandJob: Job? = null
+ override val isSatelliteProvisioned = MutableStateFlow(true)
override val connectionState = MutableStateFlow(SatelliteConnectionState.Unknown)
override val signalStrength = MutableStateFlow(0)
override val isSatelliteAllowedForCurrentLocation = MutableStateFlow(true)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
index 1449e53..ec3af87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
@@ -23,6 +23,7 @@
import android.telephony.satellite.SatelliteManager
import android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS
import android.telephony.satellite.SatelliteModemStateCallback
+import android.telephony.satellite.SatelliteProvisionStateCallback
import android.telephony.satellite.SatelliteSupportedStateCallback
import androidx.annotation.VisibleForTesting
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
@@ -337,6 +338,43 @@
}
}
+ override val isSatelliteProvisioned: StateFlow<Boolean> =
+ satelliteSupport
+ .whenSupported(
+ supported = ::satelliteProvisioned,
+ orElse = flowOf(false),
+ retrySignal = telephonyProcessCrashedEvent,
+ )
+ .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+ private fun satelliteProvisioned(sm: SupportedSatelliteManager): Flow<Boolean> =
+ conflatedCallbackFlow {
+ val callback = SatelliteProvisionStateCallback { provisioned ->
+ logBuffer.i {
+ "onSatelliteProvisionStateChanged: " +
+ if (provisioned) "provisioned" else "not provisioned"
+ }
+ trySend(provisioned)
+ }
+
+ var registered = false
+ try {
+ sm.registerForProvisionStateChanged(
+ bgDispatcher.asExecutor(),
+ callback,
+ )
+ registered = true
+ } catch (e: Exception) {
+ logBuffer.e("error registering for provisioning state callback", e)
+ }
+
+ awaitClose {
+ if (registered) {
+ sm.unregisterForProvisionStateChanged(callback)
+ }
+ }
+ }
+
/**
* Signal that we should start polling [checkIsSatelliteAllowed]. We only need to poll if there
* are active listeners to [isSatelliteAllowedForCurrentLocation]
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
index b66ace6..03f88c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
@@ -27,7 +27,6 @@
import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
-import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -45,7 +44,6 @@
constructor(
val repo: DeviceBasedSatelliteRepository,
iconsInteractor: MobileIconsInteractor,
- deviceProvisioningInteractor: DeviceProvisioningInteractor,
wifiInteractor: WifiInteractor,
@Application scope: CoroutineScope,
@DeviceBasedSatelliteInputLog private val logBuffer: LogBuffer,
@@ -78,7 +76,7 @@
}
.stateIn(scope, SharingStarted.WhileSubscribed(), 0)
- val isDeviceProvisioned: Flow<Boolean> = deviceProvisioningInteractor.isDeviceProvisioned
+ val isSatelliteProvisioned = repo.isSatelliteProvisioned
val isWifiActive: Flow<Boolean> =
wifiInteractor.wifiNetwork.map { it is WifiNetworkModel.Active }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
index 0ed1b9b..48278d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
@@ -79,11 +79,11 @@
} else {
combine(
interactor.isSatelliteAllowed,
- interactor.isDeviceProvisioned,
+ interactor.isSatelliteProvisioned,
interactor.isWifiActive,
airplaneModeRepository.isAirplaneMode
- ) { isSatelliteAllowed, isDeviceProvisioned, isWifiActive, isAirplaneMode ->
- isSatelliteAllowed && isDeviceProvisioned && !isWifiActive && !isAirplaneMode
+ ) { isSatelliteAllowed, isSatelliteProvisioned, isWifiActive, isAirplaneMode ->
+ isSatelliteAllowed && isSatelliteProvisioned && !isWifiActive && !isAirplaneMode
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
index 139ac7e..a27989d 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
@@ -36,6 +36,7 @@
import dagger.multibindings.IntoSet
import java.util.Optional
import javax.inject.Named
+import javax.inject.Qualifier
import javax.inject.Scope
@Scope @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class SysUIUnfoldScope
@@ -54,8 +55,17 @@
@Module(subcomponents = [SysUIUnfoldComponent::class])
class SysUIUnfoldModule {
+ /**
+ * Qualifier for dependencies bound in [com.android.systemui.unfold.SysUIUnfoldModule]
+ */
+ @Qualifier
+ @MustBeDocumented
+ @Retention(AnnotationRetention.RUNTIME)
+ annotation class BoundFromSysUiUnfoldModule
+
@Provides
@SysUISingleton
+ @BoundFromSysUiUnfoldModule
fun provideSysUIUnfoldComponent(
provider: Optional<UnfoldTransitionProgressProvider>,
rotationProvider: Optional<NaturalRotationUnfoldProgressProvider>,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index e613216..f457470 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -136,6 +136,7 @@
import com.android.systemui.util.RoundedCornerProgressDrawable;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor;
+import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag;
import com.android.systemui.volume.ui.binder.VolumeDialogMenuIconBinder;
import com.android.systemui.volume.ui.navigation.VolumeNavigator;
@@ -313,6 +314,7 @@
private final VibratorHelper mVibratorHelper;
private final com.android.systemui.util.time.SystemClock mSystemClock;
private final VolumeDialogMenuIconBinder mVolumeDialogMenuIconBinder;
+ private final VolumePanelFlag mVolumePanelFlag;
public VolumeDialogImpl(
Context context,
@@ -328,6 +330,7 @@
CsdWarningDialog.Factory csdWarningDialogFactory,
DevicePostureController devicePostureController,
Looper looper,
+ VolumePanelFlag volumePanelFlag,
DumpManager dumpManager,
Lazy<SecureSettings> secureSettings,
VibratorHelper vibratorHelper,
@@ -366,6 +369,7 @@
mSecureSettings = secureSettings;
mVolumeDialogMenuIconBinder = volumeDialogMenuIconBinder;
mDialogTimeoutMillis = DIALOG_TIMEOUT_MILLIS;
+ mVolumePanelFlag = volumePanelFlag;
dumpManager.registerDumpable("VolumeDialogImpl", this);
@@ -1364,6 +1368,9 @@
}
private void updateODICaptionsH(boolean isServiceComponentEnabled, boolean fromTooltip) {
+ // don't show captions view when the new volume panel is enabled.
+ isServiceComponentEnabled =
+ isServiceComponentEnabled && !mVolumePanelFlag.canUseNewVolumePanel();
if (mODICaptionsView != null) {
mODICaptionsView.setVisibility(isServiceComponentEnabled ? VISIBLE : GONE);
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index fd68bfb..f8ddc42 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -44,6 +44,7 @@
import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor;
import com.android.systemui.volume.panel.dagger.VolumePanelComponent;
import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory;
+import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag;
import com.android.systemui.volume.ui.binder.VolumeDialogMenuIconBinder;
import com.android.systemui.volume.ui.navigation.VolumeNavigator;
@@ -112,6 +113,7 @@
VolumeNavigator volumeNavigator,
CsdWarningDialog.Factory csdFactory,
DevicePostureController devicePostureController,
+ VolumePanelFlag volumePanelFlag,
DumpManager dumpManager,
Lazy<SecureSettings> secureSettings,
VibratorHelper vibratorHelper,
@@ -131,6 +133,7 @@
csdFactory,
devicePostureController,
Looper.getMainLooper(),
+ volumePanelFlag,
dumpManager,
secureSettings,
vibratorHelper,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
index 6c6a1cc..324579d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
@@ -57,7 +57,7 @@
.toViewModel(
isChecked = isEnabled is SpatialAudioEnabledModel.SpatialAudioEnabled,
isHeadTrackingAvailable =
- isAvailable is SpatialAudioAvailabilityModel.SpatialAudio,
+ isAvailable is SpatialAudioAvailabilityModel.HeadTracking,
)
.copy(label = context.getString(R.string.volume_panel_spatial_audio_title))
}
@@ -69,7 +69,7 @@
// head tracking availability means there are three possible states for the spatial
// audio: disabled, enabled regular, enabled with head tracking.
// Show popup in this case instead of a togglealbe button.
- it is SpatialAudioAvailabilityModel.SpatialAudio
+ it is SpatialAudioAvailabilityModel.HeadTracking
}
.stateIn(scope, SharingStarted.Eagerly, false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java
index 5bc9aa4..cbd535b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java
@@ -21,8 +21,10 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
-import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -34,8 +36,12 @@
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
+import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.Display;
+import android.view.IRotationWatcher;
+import android.view.IWindowManager;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.View;
@@ -55,6 +61,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -73,9 +81,12 @@
private ValueAnimator mShowHideBorderAnimator;
private SurfaceControl.Transaction mTransaction;
private TestableWindowManager mWindowManager;
+ @Mock
+ private IWindowManager mIWindowManager;
@Before
public void setUp() {
+ MockitoAnnotations.initMocks(this);
getInstrumentation().runOnMainSync(() -> mSurfaceControlViewHost =
spy(new SurfaceControlViewHost(mContext, mContext.getDisplay(),
new InputTransferToken(), "FullscreenMagnification")));
@@ -88,9 +99,11 @@
mShowHideBorderAnimator = spy(newNullTargetObjectAnimator());
mFullscreenMagnificationController = new FullscreenMagnificationController(
mContext,
+ mContext.getMainThreadHandler(),
mContext.getMainExecutor(),
mContext.getSystemService(AccessibilityManager.class),
mContext.getSystemService(WindowManager.class),
+ mIWindowManager,
scvhSupplier,
mTransaction,
mShowHideBorderAnimator);
@@ -104,7 +117,8 @@
}
@Test
- public void enableFullscreenMagnification_visibleBorder() throws InterruptedException {
+ public void enableFullscreenMagnification_visibleBorder()
+ throws InterruptedException, RemoteException {
CountDownLatch transactionCommittedLatch = new CountDownLatch(1);
CountDownLatch animationEndLatch = new CountDownLatch(1);
mTransaction.addTransactionCommittedListener(
@@ -119,17 +133,21 @@
//Enable fullscreen magnification
mFullscreenMagnificationController
.onFullscreenMagnificationActivationChanged(true));
- assertTrue("Failed to wait for transaction committed",
- transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS));
- assertTrue("Failed to wait for animation to be finished",
- animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertWithMessage("Failed to wait for transaction committed")
+ .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS))
+ .isTrue();
+ assertWithMessage("Failed to wait for animation to be finished")
+ .that(animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
+ .isTrue();
verify(mShowHideBorderAnimator).start();
+ verify(mIWindowManager)
+ .watchRotation(any(IRotationWatcher.class), eq(Display.DEFAULT_DISPLAY));
assertThat(mSurfaceControlViewHost.getView().isVisibleToUser()).isTrue();
}
@Test
public void disableFullscreenMagnification_reverseAnimationAndReleaseScvh()
- throws InterruptedException {
+ throws InterruptedException, RemoteException {
CountDownLatch transactionCommittedLatch = new CountDownLatch(1);
CountDownLatch enableAnimationEndLatch = new CountDownLatch(1);
CountDownLatch disableAnimationEndLatch = new CountDownLatch(1);
@@ -149,11 +167,12 @@
//Enable fullscreen magnification
mFullscreenMagnificationController
.onFullscreenMagnificationActivationChanged(true));
- assertTrue("Failed to wait for transaction committed",
- transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS));
- assertTrue("Failed to wait for enabling animation to be finished",
- enableAnimationEndLatch.await(
- ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertWithMessage("Failed to wait for transaction committed")
+ .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS))
+ .isTrue();
+ assertWithMessage("Failed to wait for enabling animation to be finished")
+ .that(enableAnimationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
+ .isTrue();
verify(mShowHideBorderAnimator).start();
getInstrumentation().runOnMainSync(() ->
@@ -161,11 +180,12 @@
mFullscreenMagnificationController
.onFullscreenMagnificationActivationChanged(false));
- assertTrue("Failed to wait for disabling animation to be finished",
- disableAnimationEndLatch.await(
- ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertWithMessage("Failed to wait for disabling animation to be finished")
+ .that(disableAnimationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
+ .isTrue();
verify(mShowHideBorderAnimator).reverse();
verify(mSurfaceControlViewHost).release();
+ verify(mIWindowManager).removeRotationWatcher(any(IRotationWatcher.class));
}
@Test
@@ -188,10 +208,12 @@
() -> mFullscreenMagnificationController
.onFullscreenMagnificationActivationChanged(true));
- assertTrue("Failed to wait for transaction committed",
- transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS));
- assertTrue("Failed to wait for animation to be finished",
- animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertWithMessage("Failed to wait for transaction committed")
+ .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS))
+ .isTrue();
+ assertWithMessage("Failed to wait for animation to be finished")
+ .that(animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
+ .isTrue();
verify(mShowHideBorderAnimator).reverse();
}
@@ -212,10 +234,12 @@
//Enable fullscreen magnification
mFullscreenMagnificationController
.onFullscreenMagnificationActivationChanged(true));
- assertTrue("Failed to wait for transaction committed",
- transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS));
- assertTrue("Failed to wait for animation to be finished",
- animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertWithMessage("Failed to wait for transaction committed")
+ .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS))
+ .isTrue();
+ assertWithMessage("Failed to wait for animation to be finished")
+ .that(animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
+ .isTrue();
final Rect testWindowBounds = new Rect(
mWindowManager.getCurrentWindowMetrics().getBounds());
testWindowBounds.set(testWindowBounds.left, testWindowBounds.top,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
index 5bfb3cf..361a945 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
@@ -39,6 +39,7 @@
import android.provider.Settings;
import android.testing.TestableLooper;
import android.view.Display;
+import android.view.IWindowManager;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IMagnificationConnection;
import android.view.accessibility.IMagnificationConnectionCallback;
@@ -99,6 +100,8 @@
private SecureSettings mSecureSettings;
@Mock
private AccessibilityLogger mA11yLogger;
+ @Mock
+ private IWindowManager mIWindowManager;
private IMagnificationConnection mIMagnificationConnection;
private Magnification mMagnification;
@@ -117,9 +120,10 @@
mTestableLooper = TestableLooper.get(this);
assertNotNull(mTestableLooper);
mMagnification = new Magnification(getContext(),
- mTestableLooper.getLooper(), getContext().getMainExecutor(), mCommandQueue,
+ mTestableLooper.getLooper(), mContext.getMainExecutor(), mCommandQueue,
mModeSwitchesController, mSysUiState, mOverviewProxyService, mSecureSettings,
- mDisplayTracker, getContext().getSystemService(DisplayManager.class), mA11yLogger);
+ mDisplayTracker, getContext().getSystemService(DisplayManager.class),
+ mA11yLogger, mIWindowManager);
mMagnification.mWindowMagnificationControllerSupplier =
new FakeWindowMagnificationControllerSupplier(
mContext.getSystemService(DisplayManager.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
index ffba25c..17b7e21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
@@ -42,6 +42,7 @@
import android.os.RemoteException;
import android.testing.TestableLooper;
import android.view.Display;
+import android.view.IWindowManager;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IMagnificationConnection;
import android.view.accessibility.IMagnificationConnectionCallback;
@@ -93,6 +94,8 @@
private MagnificationSettingsController mMagnificationSettingsController;
@Mock
private AccessibilityLogger mA11yLogger;
+ @Mock
+ private IWindowManager mIWindowManager;
@Before
public void setUp() throws Exception {
@@ -122,10 +125,10 @@
mCommandQueue = new CommandQueue(getContext(), mDisplayTracker);
mMagnification = new Magnification(getContext(),
- getContext().getMainThreadHandler(), getContext().getMainExecutor(),
+ getContext().getMainThreadHandler(), mContext.getMainExecutor(),
mCommandQueue, mModeSwitchesController,
mSysUiState, mOverviewProxyService, mSecureSettings, mDisplayTracker,
- getContext().getSystemService(DisplayManager.class), mA11yLogger);
+ getContext().getSystemService(DisplayManager.class), mA11yLogger, mIWindowManager);
mMagnification.mWindowMagnificationControllerSupplier = new FakeControllerSupplier(
mContext.getSystemService(DisplayManager.class), mWindowMagnificationController);
mMagnification.mMagnificationSettingsSupplier = new FakeSettingsSupplier(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
index 7c92ede..42ab25f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
@@ -48,6 +48,7 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
import com.android.systemui.testKosmos
import kotlin.test.Test
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -78,6 +79,9 @@
fun setup() {
underTest.start()
+ kosmos.fakeKeyguardRepository.setDreaming(true)
+ kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(true)
+
// Transition to DOZING and set the power interactor asleep.
powerInteractor.setAsleepForTest()
runBlocking {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt
index 88fe4ce..af76b08 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt
@@ -16,12 +16,18 @@
package com.android.systemui.keyguard.domain.interactor
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.AuthenticationFlags
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -81,6 +87,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
fun testTransitionsToLockscreen_ifFinishedInGone() =
testScope.runTest {
keyguardTransitionRepository.sendTransitionSteps(
@@ -92,7 +99,34 @@
kosmos.fakeKeyguardRepository.setKeyguardShowing(true)
runCurrent()
- // We're in the middle of a LOCKSCREEN -> GONE transition.
+ // We're in the middle of a GONE -> LOCKSCREEN transition.
+ assertThat(keyguardTransitionRepository)
+ .startedTransition(
+ to = KeyguardState.LOCKSCREEN,
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionsToLockscreen_ifFinishedInGone_wmRefactor() =
+ testScope.runTest {
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ testScope,
+ )
+ reset(keyguardTransitionRepository)
+
+ // Trigger lockdown.
+ kosmos.fakeBiometricSettingsRepository.setAuthenticationFlags(
+ AuthenticationFlags(
+ 0,
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
+ )
+ )
+ runCurrent()
+
+ // We're in the middle of a GONE -> LOCKSCREEN transition.
assertThat(keyguardTransitionRepository)
.startedTransition(
to = KeyguardState.LOCKSCREEN,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
index 1f13298..4e1b12f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
@@ -100,9 +100,11 @@
}
@Test
- fun testAodVisible_noLockscreenShownCallYet_defaultsToShowLockscreen() {
+ fun testAodVisible_noLockscreenShownCallYet_doesNotShowLockscreenUntilLater() {
underTest.setAodVisible(false)
+ verifyNoMoreInteractions(activityTaskManagerService)
+ underTest.setLockscreenShown(true)
verify(activityTaskManagerService).setLockScreenShown(true, false)
verifyNoMoreInteractions(activityTaskManagerService)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
index 8471fe1..064cf09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
@@ -663,6 +663,7 @@
true
)
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+ repository.setOrderedMedia()
assertThat(currentMedia).containsExactly(controlCommonModel)
verify(listener)
@@ -706,6 +707,7 @@
true
)
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+ repository.setOrderedMedia()
assertThat(currentMedia).containsExactly(controlCommonModel)
verify(listener)
.onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
@@ -760,6 +762,7 @@
)
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+ repository.setOrderedMedia()
assertThat(currentMedia).containsExactly(controlCommonModel)
verify(listener)
@@ -834,6 +837,7 @@
)
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+ repository.setOrderedMedia()
assertThat(currentMedia).containsExactly(controlCommonModel)
verify(listener)
@@ -922,6 +926,7 @@
// If there is media that was recently played but inactive
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+ repository.setOrderedMedia()
verify(listener)
.onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
@@ -986,6 +991,7 @@
// WHEN we have media that was recently played, but not currently active
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+ repository.setOrderedMedia()
verify(listener)
.onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
@@ -1039,6 +1045,7 @@
// WHEN we have media that was recently played, but not currently active
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+ repository.setOrderedMedia()
verify(listener)
.onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
index 18b4c48..3b541cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
@@ -242,7 +242,6 @@
mediaCarouselInteractor =
MediaCarouselInteractor(
applicationScope = testScope.backgroundScope,
- mediaDataRepository = mediaDataRepository,
mediaDataProcessor = mediaDataProcessor,
mediaTimeoutListener = mediaTimeoutListener,
mediaResumeListener = mediaResumeListener,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
index 42bd46f..5142730 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
@@ -21,6 +21,7 @@
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
+import android.graphics.drawable.TestStubDrawable
import android.media.MediaRoute2Info
import android.media.MediaRouter2Manager
import android.media.RoutingSessionInfo
@@ -30,6 +31,7 @@
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -89,6 +91,11 @@
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
public class MediaDeviceManagerTest : SysuiTestCase() {
+
+ private companion object {
+ val OTHER_DEVICE_ICON_STUB = TestStubDrawable()
+ }
+
@get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
private lateinit var manager: MediaDeviceManager
@@ -155,6 +162,11 @@
MediaTestUtils.emptyMediaData.copy(packageName = PACKAGE, token = session.sessionToken)
whenever(controllerFactory.create(session.sessionToken)).thenReturn(controller)
setupLeAudioConfiguration(false)
+
+ context.orCreateTestableResources.addOverride(
+ R.drawable.ic_media_home_devices,
+ OTHER_DEVICE_ICON_STUB
+ )
}
@After
@@ -414,6 +426,7 @@
assertThat(data.name).isEqualTo(REMOTE_DEVICE_NAME)
}
+ @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
@Test
fun onMediaDataLoaded_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() {
// GIVEN that MR2Manager returns null for routing session
@@ -429,6 +442,24 @@
assertThat(data.name).isNull()
}
+ @RequiresFlagsEnabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
+ @Test
+ fun onMediaDataLoaded_withRemotePlaybackInfo_noMatchingRoutingSession_returnsOtherDevice() {
+ // GIVEN that MR2Manager returns null for routing session
+ whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
+ whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
+ // WHEN a notification is added
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ // THEN the device is disabled and name and icon are set to "OTHER DEVICE".
+ val data = captureDeviceData(KEY)
+ assertThat(data.enabled).isFalse()
+ assertThat(data.name).isEqualTo(context.getString(R.string.media_seamless_other_device))
+ assertThat(data.icon).isEqualTo(OTHER_DEVICE_ICON_STUB)
+ }
+
+ @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
@Test
fun onSelectedDeviceStateChanged_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() {
// GIVEN a notif is added
@@ -449,7 +480,30 @@
assertThat(data.enabled).isFalse()
assertThat(data.name).isNull()
}
+ @RequiresFlagsEnabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
+ @Test
+ fun onSelectedDeviceStateChanged_withRemotePlaybackInfo_noMatchingRoutingSession_returnOtherDevice() {
+ // GIVEN a notif is added
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ reset(listener)
+ // AND MR2Manager returns null for routing session
+ whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
+ whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
+ // WHEN the selected device changes state
+ val deviceCallback = captureCallback()
+ deviceCallback.onSelectedDeviceStateChanged(device, 1)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ // THEN the device is disabled and name and icon are set to "OTHER DEVICE".
+ val data = captureDeviceData(KEY)
+ assertThat(data.enabled).isFalse()
+ assertThat(data.name).isEqualTo(context.getString(R.string.media_seamless_other_device))
+ assertThat(data.icon).isEqualTo(OTHER_DEVICE_ICON_STUB)
+ }
+ @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
@Test
fun onDeviceListUpdate_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() {
// GIVEN a notif is added
@@ -471,6 +525,29 @@
assertThat(data.name).isNull()
}
+ @RequiresFlagsEnabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
+ @Test
+ fun onDeviceListUpdate_withRemotePlaybackInfo_noMatchingRoutingSession_returnsOtherDevice() {
+ // GIVEN a notif is added
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ reset(listener)
+ // GIVEN that MR2Manager returns null for routing session
+ whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
+ whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
+ // WHEN the selected device changes state
+ val deviceCallback = captureCallback()
+ deviceCallback.onDeviceListUpdate(mutableListOf(device))
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ // THEN device is disabled and name and icon are set to "OTHER DEVICE".
+ val data = captureDeviceData(KEY)
+ assertThat(data.enabled).isFalse()
+ assertThat(data.name).isEqualTo(context.getString(R.string.media_seamless_other_device))
+ assertThat(data.icon).isEqualTo(OTHER_DEVICE_ICON_STUB)
+ }
+
// With the flag enabled, MediaDeviceManager no longer gathers device name information directly.
@RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
index 6a2637d..ccf926a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
@@ -193,11 +193,12 @@
whenever(panel.mediaViewController).thenReturn(mediaViewController)
whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false)
MediaPlayerData.clear()
+ FakeExecutor.exhaustExecutors(bgExecutor)
verify(globalSettings)
- .registerContentObserverSync(
- eq(Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE)),
- capture(settingsObserverCaptor)
- )
+ .registerContentObserverSync(
+ eq(Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE)),
+ capture(settingsObserverCaptor)
+ )
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
index d24d87c6..890a2e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
@@ -34,6 +34,7 @@
import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN
import android.telephony.satellite.SatelliteManager.SatelliteException
import android.telephony.satellite.SatelliteModemStateCallback
+import android.telephony.satellite.SatelliteProvisionStateCallback
import android.telephony.satellite.SatelliteSupportedStateCallback
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -326,6 +327,98 @@
}
@Test
+ fun satelliteProvisioned_notSupported_defaultFalse() =
+ testScope.runTest {
+ // GIVEN satellite is not supported
+ setUpRepo(
+ uptime = MIN_UPTIME,
+ satMan = satelliteManager,
+ satelliteSupported = false,
+ )
+
+ assertThat(underTest.isSatelliteProvisioned.value).isFalse()
+ }
+
+ @Test
+ fun satelliteProvisioned_supported_defaultFalse() =
+ testScope.runTest {
+ // GIVEN satellite is supported
+ setUpRepo(
+ uptime = MIN_UPTIME,
+ satMan = satelliteManager,
+ satelliteSupported = true,
+ )
+
+ // THEN default provisioned state is false
+ assertThat(underTest.isSatelliteProvisioned.value).isFalse()
+ }
+
+ @Test
+ fun satelliteProvisioned_supported_tracksCallback() =
+ testScope.runTest {
+ // GIVEN satellite is not supported
+ setUpRepo(
+ uptime = MIN_UPTIME,
+ satMan = satelliteManager,
+ satelliteSupported = true,
+ )
+
+ val provisioned by collectLastValue(underTest.isSatelliteProvisioned)
+ runCurrent()
+
+ val callback =
+ withArgCaptor<SatelliteProvisionStateCallback> {
+ verify(satelliteManager).registerForProvisionStateChanged(any(), capture())
+ }
+
+ // WHEN provisioning state changes
+ callback.onSatelliteProvisionStateChanged(true)
+
+ // THEN the value is reflected in the repo
+ assertThat(provisioned).isTrue()
+ }
+
+ @Test
+ fun satelliteProvisioned_supported_tracksCallback_reRegistersOnCrash() =
+ testScope.runTest {
+ // GIVEN satellite is supported
+ setUpRepo(
+ uptime = MIN_UPTIME,
+ satMan = satelliteManager,
+ satelliteSupported = true,
+ )
+
+ val provisioned by collectLastValue(underTest.isSatelliteProvisioned)
+
+ runCurrent()
+
+ val callback =
+ withArgCaptor<SatelliteProvisionStateCallback> {
+ verify(satelliteManager).registerForProvisionStateChanged(any(), capture())
+ }
+ val telephonyCallback =
+ MobileTelephonyHelpers.getTelephonyCallbackForType<
+ TelephonyCallback.RadioPowerStateListener
+ >(
+ telephonyManager
+ )
+
+ // GIVEN satellite is currently provisioned
+ callback.onSatelliteProvisionStateChanged(true)
+
+ assertThat(provisioned).isTrue()
+
+ // WHEN a crash event happens (detected by radio state change)
+ telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_ON)
+ runCurrent()
+ telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_OFF)
+ runCurrent()
+
+ // THEN listeners are re-registered
+ verify(satelliteManager, times(2)).registerForProvisionStateChanged(any(), any())
+ }
+
+ @Test
fun satelliteNotSupported_listenersAreNotRegistered() =
testScope.runTest {
// GIVEN satellite is not supported
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt
index 5fa2d33..55460bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt
@@ -21,6 +21,8 @@
import kotlinx.coroutines.flow.MutableStateFlow
class FakeDeviceBasedSatelliteRepository() : DeviceBasedSatelliteRepository {
+ override val isSatelliteProvisioned = MutableStateFlow(true)
+
override val connectionState = MutableStateFlow(Off)
override val signalStrength = MutableStateFlow(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
index d303976..2e5ebb3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
@@ -31,8 +31,6 @@
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
-import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
-import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
@@ -55,9 +53,6 @@
)
private val repo = FakeDeviceBasedSatelliteRepository()
- private val deviceProvisionedRepository = FakeDeviceProvisioningRepository()
- private val deviceProvisioningInteractor =
- DeviceProvisioningInteractor(deviceProvisionedRepository)
private val connectivityRepository = FakeConnectivityRepository()
private val wifiRepository = FakeWifiRepository()
private val wifiInteractor =
@@ -69,7 +64,6 @@
DeviceBasedSatelliteInteractor(
repo,
iconsInteractor,
- deviceProvisioningInteractor,
wifiInteractor,
testScope.backgroundScope,
FakeLogBuffer.Factory.create(),
@@ -113,7 +107,6 @@
DeviceBasedSatelliteInteractor(
repo,
iconsInteractor,
- deviceProvisioningInteractor,
wifiInteractor,
testScope.backgroundScope,
FakeLogBuffer.Factory.create(),
@@ -162,7 +155,6 @@
DeviceBasedSatelliteInteractor(
repo,
iconsInteractor,
- deviceProvisioningInteractor,
wifiInteractor,
testScope.backgroundScope,
FakeLogBuffer.Factory.create(),
@@ -219,7 +211,6 @@
DeviceBasedSatelliteInteractor(
repo,
iconsInteractor,
- deviceProvisioningInteractor,
wifiInteractor,
testScope.backgroundScope,
FakeLogBuffer.Factory.create(),
@@ -538,7 +529,6 @@
DeviceBasedSatelliteInteractor(
repo,
iconsInteractor,
- deviceProvisioningInteractor,
wifiInteractor,
testScope.backgroundScope,
FakeLogBuffer.Factory.create(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
index 43b9568..c39e301 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
@@ -32,8 +32,6 @@
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
-import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
-import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
@@ -55,9 +53,6 @@
private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
- private val deviceProvisionedRepository = FakeDeviceProvisioningRepository()
- private val deviceProvisioningInteractor =
- DeviceProvisioningInteractor(deviceProvisionedRepository)
private val connectivityRepository = FakeConnectivityRepository()
private val wifiRepository = FakeWifiRepository()
private val wifiInteractor =
@@ -72,7 +67,6 @@
DeviceBasedSatelliteInteractor(
repo,
mobileIconsInteractor,
- deviceProvisioningInteractor,
wifiInteractor,
testScope.backgroundScope,
FakeLogBuffer.Factory.create(),
@@ -252,14 +246,14 @@
// GIVEN apm is disabled
airplaneModeRepository.setIsAirplaneMode(false)
- // GIVEN device is not provisioned
- deviceProvisionedRepository.setDeviceProvisioned(false)
+ // GIVEN satellite is not provisioned
+ repo.isSatelliteProvisioned.value = false
// THEN icon is null because the device is not provisioned
assertThat(latest).isNull()
- // GIVEN device becomes provisioned
- deviceProvisionedRepository.setDeviceProvisioned(true)
+ // GIVEN satellite becomes provisioned
+ repo.isSatelliteProvisioned.value = true
// Wait for delay to be completed
advanceTimeBy(10.seconds)
@@ -285,8 +279,8 @@
// GIVEN apm is disabled
airplaneModeRepository.setIsAirplaneMode(false)
- // GIVEN device is provisioned
- deviceProvisionedRepository.setDeviceProvisioned(true)
+ // GIVEN satellite is provisioned
+ repo.isSatelliteProvisioned.value = true
// GIVEN wifi network is active
wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 0, level = 1))
@@ -474,14 +468,14 @@
// GIVEN apm is disabled
airplaneModeRepository.setIsAirplaneMode(false)
- // GIVEN device is not provisioned
- deviceProvisionedRepository.setDeviceProvisioned(false)
+ // GIVEN satellite is not provisioned
+ repo.isSatelliteProvisioned.value = false
// THEN carrier text is null because the device is not provisioned
assertThat(latest).isNull()
- // GIVEN device becomes provisioned
- deviceProvisionedRepository.setDeviceProvisioned(true)
+ // GIVEN satellite becomes provisioned
+ repo.isSatelliteProvisioned.value = true
// Wait for delay to be completed
advanceTimeBy(10.seconds)
@@ -508,8 +502,8 @@
// GIVEN apm is disabled
airplaneModeRepository.setIsAirplaneMode(false)
- // GIVEN device is provisioned
- deviceProvisionedRepository.setDeviceProvisioned(true)
+ // GIVEN satellite is provisioned
+ repo.isSatelliteProvisioned.value = true
// GIVEN wifi network is active
wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 0, level = 1))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index dbdbe65..fac6a4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -87,6 +87,7 @@
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor;
+import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag;
import com.android.systemui.volume.ui.binder.VolumeDialogMenuIconBinder;
import com.android.systemui.volume.ui.navigation.VolumeNavigator;
@@ -119,7 +120,6 @@
View mDrawerNormal;
ViewGroup mDialogRowsView;
CaptionsToggleImageButton mODICaptionsIcon;
-
private TestableLooper mTestableLooper;
private ConfigurationController mConfigurationController;
private int mOriginalOrientation;
@@ -151,6 +151,8 @@
private VolumeNavigator mVolumeNavigator;
@Mock
private VolumeDialogMenuIconBinder mVolumeDialogMenuIconBinder;
+ @Mock
+ private VolumePanelFlag mVolumePanelFlag;
private final CsdWarningDialog.Factory mCsdWarningDialogFactory =
new CsdWarningDialog.Factory() {
@@ -211,6 +213,7 @@
mCsdWarningDialogFactory,
mPostureController,
mTestableLooper.getLooper(),
+ mVolumePanelFlag,
mDumpManager,
mLazySecureSettings,
mVibratorHelper,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt
index 5a092f3..62e56be 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt
@@ -16,16 +16,16 @@
package com.android.systemui.animation
+import android.content.applicationContext
import com.android.systemui.jank.interactionJankMonitor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.kosmos.testCase
val Kosmos.dialogTransitionAnimator by Fixture {
fakeDialogTransitionAnimator(
// The main thread is checked in a bunch of places inside the different transitions
// animators, so we have to pass the real main executor here.
- mainExecutor = testCase.context.mainExecutor,
+ mainExecutor = applicationContext.mainExecutor,
interactionJankMonitor = interactionJankMonitor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
index 162fd90..28bd439 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
@@ -16,7 +16,6 @@
package com.android.systemui.keyguard.domain.interactor
-import com.android.systemui.flags.featureFlagsClassic
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -34,7 +33,6 @@
bgDispatcher = testDispatcher,
mainDispatcher = testDispatcher,
keyguardInteractor = keyguardInteractor,
- flags = featureFlagsClassic,
shadeRepository = shadeRepository,
powerInteractor = powerInteractor,
glanceableHubTransitions = glanceableHubTransitions,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt
index 98babff..d72b9c1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt
@@ -18,7 +18,6 @@
import com.android.keyguard.keyguardSecurityModel
import com.android.systemui.communal.domain.interactor.communalInteractor
-import com.android.systemui.flags.featureFlagsClassic
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -37,7 +36,6 @@
mainDispatcher = testDispatcher,
keyguardInteractor = keyguardInteractor,
communalInteractor = communalInteractor,
- flags = featureFlagsClassic,
keyguardSecurityModel = keyguardSecurityModel,
selectedUserInteractor = selectedUserInteractor,
powerInteractor = powerInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractorKosmos.kt
index e5e2aff..ca1b3f5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractorKosmos.kt
@@ -18,7 +18,6 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.media.controls.data.repository.mediaDataRepository
import com.android.systemui.media.controls.data.repository.mediaFilterRepository
import com.android.systemui.media.controls.domain.pipeline.mediaDataCombineLatest
import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter
@@ -33,7 +32,6 @@
Kosmos.Fixture {
MediaCarouselInteractor(
applicationScope = applicationCoroutineScope,
- mediaDataRepository = mediaDataRepository,
mediaDataProcessor = mediaDataProcessor,
mediaTimeoutListener = mediaTimeoutListener,
mediaResumeListener = mediaResumeListener,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopupKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopupKosmos.kt
new file mode 100644
index 0000000..49170d8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopupKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.popup.ui.composable
+
+import com.android.systemui.animation.dialogTransitionAnimator
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.phone.systemUIDialogFactory
+
+val Kosmos.volumePanelPopup: VolumePanelPopup by
+ Kosmos.Fixture { VolumePanelPopup(systemUIDialogFactory, dialogTransitionAnimator) }
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
index 4f3aee9..fec6ff1 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
@@ -27,6 +27,7 @@
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import java.util.concurrent.CopyOnWriteArrayList
/**
* Allows to subscribe to rotation changes. Updates are provided for the display associated to
@@ -41,7 +42,7 @@
@Assisted private val callbackHandler: Handler,
) : CallbackController<RotationChangeProvider.RotationListener> {
- private val listeners = mutableListOf<RotationListener>()
+ private val listeners = CopyOnWriteArrayList<RotationListener>()
private val displayListener = RotationDisplayListener()
private var lastRotation: Int? = null
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 78edb8e..1831ecd 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -689,12 +689,20 @@
Slog.v(TAG, "AutofillWindowPresenter.show(): fit=" + fitsSystemWindows
+ ", params=" + paramsToString(p));
}
- UiThread.getHandler().post(() -> mWindow.show(p));
+ UiThread.getHandler().post(() -> {
+ if (mWindow != null) {
+ mWindow.show(p);
+ }
+ });
}
@Override
public void hide(Rect transitionEpicenter) {
- UiThread.getHandler().post(mWindow::hide);
+ UiThread.getHandler().post(() -> {
+ if (mWindow != null) {
+ mWindow.hide();
+ }
+ });
}
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 167c384..53730e3 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -227,6 +227,7 @@
"connectivity_flags_lib",
"dreams_flags_lib",
"aconfig_new_storage_flags_lib",
+ "powerstats_flags_lib",
],
javac_shard_size: 50,
javacflags: [
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 0c1d0fb..e424ffa 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3892,9 +3892,10 @@
return;
}
- final long lastTopTime = sr.app.mState.getLastTopTime();
- final long constantTimeLimit = getTimeLimitForFgsType(fgsType);
+ final boolean currentlyTop = sr.app.mState.getCurProcState() <= PROCESS_STATE_TOP;
final long nowUptime = SystemClock.uptimeMillis();
+ final long lastTopTime = currentlyTop ? nowUptime : sr.app.mState.getLastTopTime();
+ final long constantTimeLimit = getTimeLimitForFgsType(fgsType);
if (lastTopTime != Long.MIN_VALUE && constantTimeLimit > (nowUptime - lastTopTime)) {
// Discard any other messages for this service
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_FGS_TIMEOUT_MSG, sr);
@@ -6290,7 +6291,7 @@
final ComponentName clientSideComponentName =
cr.aliasComponent != null ? cr.aliasComponent : r.name;
try {
- cr.conn.connected(r.name, null, true);
+ cr.conn.connected(clientSideComponentName, null, true);
} catch (Exception e) {
Slog.w(TAG, "Failure disconnecting service " + r.shortInstanceName
+ " to connection " + c.get(i).conn.asBinder()
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 26aa053..2c04883 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -169,6 +169,11 @@
*/
static final String KEY_ENABLE_NEW_OOMADJ = "enable_new_oom_adj";
+ /**
+ * Whether or not to enable the batching of OOM adjuster calls to LMKD
+ */
+ static final String KEY_ENABLE_BATCHING_OOM_ADJ = "enable_batching_oom_adj";
+
private static final int DEFAULT_MAX_CACHED_PROCESSES = 1024;
private static final boolean DEFAULT_PRIORITIZE_ALARM_BROADCASTS = true;
private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000;
@@ -244,6 +249,11 @@
private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = Flags.oomadjusterCorrectnessRewrite();
/**
+ * The default value to {@link #KEY_ENABLE_BATCHING_OOM_ADJ}.
+ */
+ private static final boolean DEFAULT_ENABLE_BATCHING_OOM_ADJ = Flags.batchingOomAdj();
+
+ /**
* Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
*/
private static final int
@@ -1136,6 +1146,9 @@
/** @see #KEY_ENABLE_NEW_OOMADJ */
public boolean ENABLE_NEW_OOMADJ = DEFAULT_ENABLE_NEW_OOM_ADJ;
+ /** @see #KEY_ENABLE_BATCHING_OOM_ADJ */
+ public boolean ENABLE_BATCHING_OOM_ADJ = DEFAULT_ENABLE_BATCHING_OOM_ADJ;
+
/**
* Indicates whether PSS profiling in AppProfiler is disabled or not.
*/
@@ -1479,6 +1492,8 @@
private void loadNativeBootDeviceConfigConstants() {
ENABLE_NEW_OOMADJ = getDeviceConfigBoolean(KEY_ENABLE_NEW_OOMADJ,
DEFAULT_ENABLE_NEW_OOM_ADJ);
+ ENABLE_BATCHING_OOM_ADJ = getDeviceConfigBoolean(KEY_ENABLE_BATCHING_OOM_ADJ,
+ DEFAULT_ENABLE_BATCHING_OOM_ADJ);
}
public void setOverrideMaxCachedProcesses(int value) {
@@ -2248,6 +2263,13 @@
mDefaultPssToRssThresholdModifier);
}
+ private void updateEnableBatchingOomAdj() {
+ ENABLE_BATCHING_OOM_ADJ = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
+ KEY_ENABLE_BATCHING_OOM_ADJ,
+ DEFAULT_ENABLE_BATCHING_OOM_ADJ);
+ }
+
boolean shouldDebugUidForProcState(int uid) {
SparseBooleanArray ar = mProcStateDebugUids;
final var size = ar.size();
@@ -2476,6 +2498,9 @@
pw.print(" "); pw.print(KEY_MAX_PREVIOUS_TIME);
pw.print("="); pw.println(MAX_PREVIOUS_TIME);
+ pw.print(" "); pw.print(KEY_ENABLE_BATCHING_OOM_ADJ);
+ pw.print("="); pw.println(ENABLE_BATCHING_OOM_ADJ);
+
pw.println();
if (mOverrideMaxCachedProcesses >= 0) {
pw.print(" mOverrideMaxCachedProcesses="); pw.println(mOverrideMaxCachedProcesses);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 73253b2..44e522f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3933,11 +3933,28 @@
+ packageName + ": " + e);
}
if (mUserController.isUserRunning(user, userRunningFlags)) {
+
+ String description;
+ if (reason == null) {
+ description = "from pid " + callingPid;
+
+ // Add the name of the process if it's available
+ final ProcessRecord callerApp;
+ synchronized (mPidsSelfLocked) {
+ callerApp = mPidsSelfLocked.get(callingPid);
+ }
+ if (callerApp != null) {
+ description += " (" + callerApp.processName + ")";
+ }
+ } else {
+ description = reason;
+ }
+
forceStopPackageLocked(packageName, UserHandle.getAppId(pkgUid),
false /* callerWillRestart */, false /* purgeCache */,
true /* doIt */, false /* evenPersistent */,
- false /* uninstalling */, true /* packageStateStopped */, user,
- reason == null ? ("from pid " + callingPid) : reason);
+ false /* uninstalling */, true /* packageStateStopped */,
+ user, description);
finishForceStopPackageLocked(packageName, pkgUid);
}
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index ab34dd4..fc81d3e 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -384,6 +384,13 @@
protected final ArraySet<ProcessRecord> mProcessesInCycle = new ArraySet<>();
/**
+ * List of processes that we want to batch for LMKD to adjust their respective
+ * OOM scores.
+ */
+ @GuardedBy("mService")
+ protected final ArrayList<ProcessRecord> mProcsToOomAdj = new ArrayList<ProcessRecord>();
+
+ /**
* Flag to mark if there is an ongoing oomAdjUpdate: potentially the oomAdjUpdate
* could be called recursively because of the indirect calls during the update;
* however the oomAdjUpdate itself doesn't support recursion - in this case we'd
@@ -1246,7 +1253,7 @@
if (!app.isKilledByAm() && app.getThread() != null) {
// We don't need to apply the update for the process which didn't get computed
if (state.getCompletedAdjSeq() == mAdjSeq) {
- applyOomAdjLSP(app, doingAll, now, nowElapsed, oomAdjReason);
+ applyOomAdjLSP(app, doingAll, now, nowElapsed, oomAdjReason, true);
}
if (app.isPendingFinishAttach()) {
@@ -1348,6 +1355,11 @@
}
}
+ if (!mProcsToOomAdj.isEmpty()) {
+ ProcessList.batchSetOomAdj(mProcsToOomAdj);
+ mProcsToOomAdj.clear();
+ }
+
if (proactiveKillsEnabled // Proactive kills enabled?
&& doKillExcessiveProcesses // Should kill excessive processes?
&& freeSwapPercent < lowSwapThresholdPercent // Swap below threshold?
@@ -3246,10 +3258,16 @@
mCachedAppOptimizer.onWakefulnessChanged(wakefulness);
}
+ @GuardedBy({"mService", "mProcLock"})
+ protected boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now,
+ long nowElapsed, @OomAdjReason int oomAdjReason) {
+ return applyOomAdjLSP(app, doingAll, now, nowElapsed, oomAdjReason, false);
+ }
+
/** Applies the computed oomadj, procstate and sched group values and freezes them in set* */
@GuardedBy({"mService", "mProcLock"})
protected boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now,
- long nowElapsed, @OomAdjReason int oomAdjReson) {
+ long nowElapsed, @OomAdjReason int oomAdjReson, boolean isBatchingOomAdj) {
boolean success = true;
final ProcessStateRecord state = app.mState;
final UidRecord uidRec = app.getUidRecord();
@@ -3266,7 +3284,12 @@
final int oldOomAdj = state.getSetAdj();
if (state.getCurAdj() != state.getSetAdj()) {
- ProcessList.setOomAdj(app.getPid(), app.uid, state.getCurAdj());
+ if (isBatchingOomAdj && mConstants.ENABLE_BATCHING_OOM_ADJ) {
+ mProcsToOomAdj.add(app);
+ } else {
+ ProcessList.setOomAdj(app.getPid(), app.uid, state.getCurAdj());
+ }
+
if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) {
String msg = "Set " + app.getPid() + " " + app.processName + " adj "
+ state.getCurAdj() + ": " + state.getAdjType();
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 219de70..c094724 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -354,6 +354,7 @@
// LMK_KILL_OCCURRED
// LMK_START_MONITORING
// LMK_BOOT_COMPLETED
+ // LMK_PROCS_PRIO
static final byte LMK_TARGET = 0;
static final byte LMK_PROCPRIO = 1;
static final byte LMK_PROCREMOVE = 2;
@@ -365,6 +366,7 @@
static final byte LMK_KILL_OCCURRED = 8; // Msg to subscribed clients on kill occurred event
static final byte LMK_START_MONITORING = 9; // Start monitoring if delayed earlier
static final byte LMK_BOOT_COMPLETED = 10;
+ static final byte LMK_PROCS_PRIO = 11; // Batch option for LMK_PROCPRIO
// Low Memory Killer Daemon command codes.
// These must be kept in sync with async_event_type definitions in lmkd.h
@@ -1561,6 +1563,50 @@
}
}
+
+ // The max size for PROCS_PRIO cmd in LMKD
+ private static final int MAX_PROCS_PRIO_PACKET_SIZE = 3;
+
+ // (4 bytes per field * 4 fields * 3 processes per batch) + 4 bytes for the LMKD cmd
+ private static final int MAX_OOM_ADJ_BATCH_LENGTH = ((4 * 4) * MAX_PROCS_PRIO_PACKET_SIZE) + 4;
+
+ /**
+ * Set the out-of-memory badness adjustment for a list of processes.
+ *
+ * @param apps App list to adjust their respective oom score.
+ *
+ * {@hide}
+ */
+ public static void batchSetOomAdj(ArrayList<ProcessRecord> apps) {
+ final int totalApps = apps.size();
+ if (totalApps == 0) {
+ return;
+ }
+
+ ByteBuffer buf = ByteBuffer.allocate(MAX_OOM_ADJ_BATCH_LENGTH);
+ int total_procs_in_buf = 0;
+ buf.putInt(LMK_PROCS_PRIO);
+ for (int i = 0; i < totalApps; i++) {
+ final int pid = apps.get(i).getPid();
+ final int amt = apps.get(i).mState.getCurAdj();
+ final int uid = apps.get(i).uid;
+ if (pid <= 0 || amt == UNKNOWN_ADJ) continue;
+ if (total_procs_in_buf >= MAX_PROCS_PRIO_PACKET_SIZE) {
+ writeLmkd(buf, null);
+ buf.clear();
+ total_procs_in_buf = 0;
+ buf.allocate(MAX_OOM_ADJ_BATCH_LENGTH);
+ buf.putInt(LMK_PROCS_PRIO);
+ }
+ buf.putInt(pid);
+ buf.putInt(uid);
+ buf.putInt(amt);
+ buf.putInt(0); // Default proc type to PROC_TYPE_APP
+ total_procs_in_buf++;
+ }
+ writeLmkd(buf, null);
+ }
+
/*
* {@hide}
*/
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index afde4f7..2abfad9 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -134,3 +134,11 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "batching_oom_adj"
+ namespace: "backstage_power"
+ description: "Batch OOM adjustment calls to LMKD"
+ bug: "244232958"
+ is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java
index e6de14b..16514fa 100644
--- a/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java
+++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java
@@ -29,6 +29,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -121,8 +122,7 @@
+ " without permission " + Manifest.permission.DUMP);
return;
}
- android.util.IndentingPrintWriter radioPrintWriter =
- new android.util.IndentingPrintWriter(printWriter);
+ IndentingPrintWriter radioPrintWriter = new IndentingPrintWriter(printWriter);
radioPrintWriter.printf("BroadcastRadioService\n");
radioPrintWriter.increaseIndent();
diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
index 93fb7b2..ab08342 100644
--- a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
+++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
@@ -26,6 +26,7 @@
import android.hardware.radio.RadioManager;
import android.os.Binder;
import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
@@ -138,7 +139,7 @@
+ " without permission " + Manifest.permission.DUMP);
return;
}
- android.util.IndentingPrintWriter radioPw = new android.util.IndentingPrintWriter(pw);
+ IndentingPrintWriter radioPw = new IndentingPrintWriter(pw);
radioPw.printf("BroadcastRadioService\n");
radioPw.increaseIndent();
diff --git a/services/core/java/com/android/server/broadcastradio/RadioEventLogger.java b/services/core/java/com/android/server/broadcastradio/RadioEventLogger.java
index 2c8f499..b71589c 100644
--- a/services/core/java/com/android/server/broadcastradio/RadioEventLogger.java
+++ b/services/core/java/com/android/server/broadcastradio/RadioEventLogger.java
@@ -17,6 +17,7 @@
package com.android.server.broadcastradio;
import android.text.TextUtils;
+import android.util.IndentingPrintWriter;
import android.util.LocalLog;
import android.util.Log;
@@ -54,7 +55,7 @@
* Dump broadcast radio service event
* @param pw Indenting print writer for dump
*/
- public void dump(android.util.IndentingPrintWriter pw) {
+ public void dump(IndentingPrintWriter pw) {
mEventLogger.dump(pw);
}
}
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java b/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java
index 9654a93..b618aa3 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java
@@ -22,6 +22,7 @@
import android.hardware.radio.ICloseHandle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -93,7 +94,7 @@
if (mCloseHandle != null) mCloseHandle.close();
}
- public void dumpInfo(android.util.IndentingPrintWriter pw) {
+ public void dumpInfo(IndentingPrintWriter pw) {
pw.printf("ModuleWatcher:\n");
pw.increaseIndent();
@@ -191,8 +192,7 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
- android.util.IndentingPrintWriter announcementPrintWriter =
- new android.util.IndentingPrintWriter(printWriter);
+ IndentingPrintWriter announcementPrintWriter = new IndentingPrintWriter(printWriter);
announcementPrintWriter.printf("AnnouncementAggregator\n");
announcementPrintWriter.increaseIndent();
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
index 1c42161..d9f8588 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
@@ -29,6 +29,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.SparseArray;
@@ -128,7 +129,7 @@
if (entry.getValue() == mModuleId) {
Slogf.w(TAG, "Service %s died, removed RadioModule with ID %d",
entry.getKey(), mModuleId);
- return;
+ break;
}
}
}
@@ -260,7 +261,7 @@
*
* @param pw The file to which {@link BroadcastRadioServiceImpl} state is dumped.
*/
- public void dumpInfo(android.util.IndentingPrintWriter pw) {
+ public void dumpInfo(IndentingPrintWriter pw) {
synchronized (mLock) {
pw.printf("Next module id available: %d\n", mNextModuleId);
pw.printf("ServiceName to module id map:\n");
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
index 5b77c52..077e8ee 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
@@ -446,6 +446,7 @@
sel.secondaryIds[i]);
if (id == null) {
Slogf.e(TAG, "invalid secondary id: %s", sel.secondaryIds[i]);
+ continue;
}
secondaryIdList.add(id);
}
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
index 0cac356..03e347a 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
@@ -38,6 +38,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -524,7 +525,7 @@
return BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length);
}
- void dumpInfo(android.util.IndentingPrintWriter pw) {
+ void dumpInfo(IndentingPrintWriter pw) {
pw.printf("RadioModule\n");
pw.increaseIndent();
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
index 925f149..e90a1dd 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
@@ -29,6 +29,7 @@
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
import com.android.server.broadcastradio.RadioEventLogger;
@@ -434,7 +435,7 @@
}
}
- void dumpInfo(android.util.IndentingPrintWriter pw) {
+ void dumpInfo(IndentingPrintWriter pw) {
pw.printf("TunerSession\n");
pw.increaseIndent();
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
index e1650c2..a4efa2e 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
@@ -30,6 +30,7 @@
import android.os.IHwBinder.DeathRecipient;
import android.os.RemoteException;
import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -115,7 +116,7 @@
if (entry.getValue() == moduleId) {
Slogf.i(TAG, "service " + entry.getKey()
+ " died; removed RadioModule with ID " + moduleId);
- return;
+ break;
}
}
}
@@ -221,7 +222,7 @@
*
* @param pw The file to which BroadcastRadioService state is dumped.
*/
- public void dumpInfo(android.util.IndentingPrintWriter pw) {
+ public void dumpInfo(IndentingPrintWriter pw) {
synchronized (mLock) {
pw.printf("Next module id available: %d\n", mNextModuleId);
pw.printf("ServiceName to module id map:\n");
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
index 34bfa6c..02a9f09 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
@@ -304,10 +304,7 @@
private static boolean isEmpty(
@NonNull android.hardware.broadcastradio.V2_0.ProgramSelector sel) {
- if (sel.primaryId.type != 0) return false;
- if (sel.primaryId.value != 0) return false;
- if (!sel.secondaryIds.isEmpty()) return false;
- return true;
+ return sel.primaryId.type == 0 && sel.primaryId.value == 0 && sel.secondaryIds.isEmpty();
}
static @Nullable ProgramSelector programSelectorFromHal(
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index 7269f24..d3b2448 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -40,6 +40,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
import android.util.MutableInt;
import com.android.internal.annotations.GuardedBy;
@@ -453,7 +454,7 @@
return BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length);
}
- void dumpInfo(android.util.IndentingPrintWriter pw) {
+ void dumpInfo(IndentingPrintWriter pw) {
pw.printf("RadioModule\n");
pw.increaseIndent();
pw.printf("BroadcastRadioService: %s\n", mService);
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index b1b5d34..80efacd 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -31,6 +31,7 @@
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
import android.util.MutableBoolean;
import android.util.MutableInt;
@@ -324,9 +325,7 @@
try {
isConfigFlagSet(flag);
return true;
- } catch (IllegalStateException ex) {
- return true;
- } catch (UnsupportedOperationException ex) {
+ } catch (IllegalStateException | UnsupportedOperationException ex) {
return false;
}
}
@@ -389,7 +388,7 @@
}
}
- void dumpInfo(android.util.IndentingPrintWriter pw) {
+ void dumpInfo(IndentingPrintWriter pw) {
pw.printf("TunerSession\n");
pw.increaseIndent();
pw.printf("HIDL HAL Session: %s\n", mHwSession);
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index b1b1dba..93bd926 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -212,24 +212,46 @@
public static final int TOUCH_VIRTUAL = 3;
/**
- * Diff result: The {@link #state} or {@link #committedState} fields differ.
- */
- public static final int DIFF_STATE = 1 << 0;
-
- /**
* Diff result: Other fields differ.
*/
- public static final int DIFF_OTHER = 1 << 1;
+ public static final int DIFF_OTHER = 1 << 0;
+
+ /**
+ * Diff result: The {@link #state} or {@link #committedState} fields differ.
+ */
+ public static final int DIFF_STATE = 1 << 1;
+
+ /**
+ * Diff result: The committed state differs. Note this is slightly different from the state,
+ * which is what most of the device should care about.
+ */
+ public static final int DIFF_COMMITTED_STATE = 1 << 2;
/**
* Diff result: The color mode fields differ.
*/
- public static final int DIFF_COLOR_MODE = 1 << 2;
+ public static final int DIFF_COLOR_MODE = 1 << 3;
/**
* Diff result: The hdr/sdr ratio differs
*/
- public static final int DIFF_HDR_SDR_RATIO = 1 << 3;
+ public static final int DIFF_HDR_SDR_RATIO = 1 << 4;
+
+ /**
+ * Diff result: The rotation differs
+ */
+ public static final int DIFF_ROTATION = 1 << 5;
+
+ /**
+ * Diff result: The render timings. Note this could be any of {@link #renderFrameRate},
+ * {@link #presentationDeadlineNanos}, or {@link #appVsyncOffsetNanos}.
+ */
+ public static final int DIFF_RENDER_TIMINGS = 1 << 6;
+
+ /**
+ * Diff result: The mode ID differs.
+ */
+ public static final int DIFF_MODE_ID = 1 << 7;
/**
* Diff result: Catch-all for "everything changed"
@@ -462,21 +484,33 @@
*/
public int diff(DisplayDeviceInfo other) {
int diff = 0;
- if (state != other.state || committedState != other.committedState) {
+ if (state != other.state) {
diff |= DIFF_STATE;
}
+ if (committedState != other.committedState) {
+ diff |= DIFF_COMMITTED_STATE;
+ }
if (colorMode != other.colorMode) {
diff |= DIFF_COLOR_MODE;
}
if (!BrightnessSynchronizer.floatEquals(hdrSdrRatio, other.hdrSdrRatio)) {
diff |= DIFF_HDR_SDR_RATIO;
}
+ if (rotation != other.rotation) {
+ diff |= DIFF_ROTATION;
+ }
+ if (renderFrameRate != other.renderFrameRate
+ || presentationDeadlineNanos != other.presentationDeadlineNanos
+ || appVsyncOffsetNanos != other.appVsyncOffsetNanos) {
+ diff |= DIFF_RENDER_TIMINGS;
+ }
+ if (modeId != other.modeId) {
+ diff |= DIFF_MODE_ID;
+ }
if (!Objects.equals(name, other.name)
|| !Objects.equals(uniqueId, other.uniqueId)
|| width != other.width
|| height != other.height
- || modeId != other.modeId
- || renderFrameRate != other.renderFrameRate
|| defaultModeId != other.defaultModeId
|| userPreferredModeId != other.userPreferredModeId
|| !Arrays.equals(supportedModes, other.supportedModes)
@@ -487,12 +521,9 @@
|| densityDpi != other.densityDpi
|| xDpi != other.xDpi
|| yDpi != other.yDpi
- || appVsyncOffsetNanos != other.appVsyncOffsetNanos
- || presentationDeadlineNanos != other.presentationDeadlineNanos
|| flags != other.flags
|| !Objects.equals(displayCutout, other.displayCutout)
|| touch != other.touch
- || rotation != other.rotation
|| type != other.type
|| !Objects.equals(address, other.address)
|| !Objects.equals(deviceProductInfo, other.deviceProductInfo)
diff --git a/services/core/java/com/android/server/display/DisplayDeviceRepository.java b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
index 6164154..086f8a9 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceRepository.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
@@ -21,6 +21,7 @@
import android.util.Slog;
import android.view.Display;
import android.view.DisplayAddress;
+import android.view.Surface;
import com.android.internal.annotations.GuardedBy;
import com.android.server.display.DisplayManagerService.SyncRoot;
@@ -179,6 +180,20 @@
if (diff == DisplayDeviceInfo.DIFF_STATE) {
Slog.i(TAG, "Display device changed state: \"" + info.name
+ "\", " + Display.stateToString(info.state));
+ } else if (diff == DisplayDeviceInfo.DIFF_ROTATION) {
+ Slog.i(TAG, "Display device rotated: \"" + info.name
+ + "\", " + Surface.rotationToString(info.rotation));
+ } else if (diff
+ == (DisplayDeviceInfo.DIFF_MODE_ID | DisplayDeviceInfo.DIFF_RENDER_TIMINGS)) {
+ Slog.i(TAG, "Display device changed render timings: \"" + info.name
+ + "\", renderFrameRate=" + info.renderFrameRate
+ + ", presentationDeadlineNanos=" + info.presentationDeadlineNanos
+ + ", appVsyncOffsetNanos=" + info.appVsyncOffsetNanos);
+ } else if (diff == DisplayDeviceInfo.DIFF_COMMITTED_STATE) {
+ if (DEBUG) {
+ Slog.i(TAG, "Display device changed committed state: \"" + info.name
+ + "\", " + Display.stateToString(info.committedState));
+ }
} else if (diff != DisplayDeviceInfo.DIFF_HDR_SDR_RATIO) {
Slog.i(TAG, "Display device changed: " + info);
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
index 2b5241f..b43b35b 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
@@ -102,6 +102,9 @@
private DisplayManagerFlags mDisplayManagerFlags;
+ // Indicates if the current auto-brightness should be ramped up or down slowly.
+ private boolean mIsSlowChange;
+
@VisibleForTesting
AutomaticBrightnessStrategy(Context context, int displayId, Injector injector,
DisplayManagerFlags displayManagerFlags) {
@@ -172,6 +175,11 @@
isValid = true;
}
}
+
+ // A change is slow when the auto-brightness was already applied, and there are no new
+ // auto-brightness adjustments from an external client(e.g. Moving the slider). As such,
+ // it is important to record this value before applying the current auto-brightness.
+ mIsSlowChange = hasAppliedAutoBrightness() && !getAutoBrightnessAdjustmentChanged();
setAutoBrightnessApplied(isValid);
return isValid;
}
@@ -284,8 +292,7 @@
.setSdrBrightness(brightness)
.setBrightnessReason(brightnessReason)
.setDisplayBrightnessStrategyName(getName())
- .setIsSlowChange(hasAppliedAutoBrightness()
- && !getAutoBrightnessAdjustmentChanged())
+ .setIsSlowChange(mIsSlowChange)
.setBrightnessEvent(brightnessEvent)
.setBrightnessAdjustmentFlag(mAutoBrightnessAdjustmentReasonsFlags)
.setShouldUpdateScreenBrightnessSetting(
diff --git a/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java b/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java
index 8a3a56c..fd3a92e 100644
--- a/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java
+++ b/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java
@@ -212,6 +212,16 @@
}
}
+ public void setChargingPolicy(int policy) throws RemoteException {
+ IHealth service = mLastService.get();
+ if (service == null) return;
+ try {
+ service.setChargingPolicy(policy);
+ } catch (UnsupportedOperationException | ServiceSpecificException ex) {
+ return;
+ }
+ }
+
private static void traceBegin(String name) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name);
}
diff --git a/services/core/java/com/android/server/health/OWNERS b/services/core/java/com/android/server/health/OWNERS
index 81522fc..44ab7f7 100644
--- a/services/core/java/com/android/server/health/OWNERS
+++ b/services/core/java/com/android/server/health/OWNERS
@@ -1 +1 @@
-file:platform/hardware/interfaces:/health/aidl/OWNERS
+file:platform/hardware/interfaces:/health/OWNERS
diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
index 2e44b6d..7d48527 100644
--- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
@@ -32,6 +32,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.os.IBinder;
import android.os.ResultReceiver;
import android.util.EventLog;
@@ -137,15 +138,17 @@
@GuardedBy("ImfLock.class")
@Override
public void applyImeVisibility(IBinder windowToken, @NonNull ImeTracker.Token statsToken,
- @ImeVisibilityStateComputer.VisibilityState int state) {
+ @ImeVisibilityStateComputer.VisibilityState int state, @UserIdInt int userId) {
applyImeVisibility(windowToken, statsToken, state,
- SoftInputShowHideReason.NOT_SET /* ignore reason */);
+ SoftInputShowHideReason.NOT_SET /* ignore reason */, userId);
}
@GuardedBy("ImfLock.class")
void applyImeVisibility(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
@ImeVisibilityStateComputer.VisibilityState int state,
- @SoftInputShowHideReason int reason) {
+ @SoftInputShowHideReason int reason, @UserIdInt int userId) {
+ final var bindingController = mService.getInputMethodBindingController(userId);
+ final int displayIdToShowIme = bindingController.getDisplayIdToShowIme();
switch (state) {
case STATE_SHOW_IME:
if (!Flags.refactorInsetsController()) {
@@ -165,8 +168,7 @@
// NOT_FOCUSABLE, ALT_FOCUSABLE_IM flags set and can the IME target.
// Send it to window manager to hide IME from the actual IME control target
// of the target display.
- mWindowManagerInternal.hideIme(windowToken,
- mService.getDisplayIdToShowImeLocked(), statsToken);
+ mWindowManagerInternal.hideIme(windowToken, displayIdToShowIme, statsToken);
} else {
ImeTracker.forLogging().onFailed(statsToken,
ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY);
@@ -201,10 +203,10 @@
}
break;
case STATE_SHOW_IME_SNAPSHOT:
- showImeScreenshot(windowToken, mService.getDisplayIdToShowImeLocked());
+ showImeScreenshot(windowToken, displayIdToShowIme);
break;
case STATE_REMOVE_IME_SNAPSHOT:
- removeImeScreenshot(mService.getDisplayIdToShowImeLocked());
+ removeImeScreenshot(displayIdToShowIme);
break;
default:
throw new IllegalArgumentException("Invalid IME visibility state: " + state);
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
index 9f2b84d..a5f9b7a 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
@@ -17,6 +17,7 @@
package com.android.server.inputmethod;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.os.IBinder;
import android.os.ResultReceiver;
import android.view.inputmethod.ImeTracker;
@@ -63,7 +64,7 @@
* @param state The new IME visibility state for the applier to handle
*/
default void applyImeVisibility(IBinder windowToken, @NonNull ImeTracker.Token statsToken,
- @ImeVisibilityStateComputer.VisibilityState int state) {}
+ @ImeVisibilityStateComputer.VisibilityState int state, @UserIdInt int userId) {}
/**
* Updates the IME Z-ordering relative to the given window.
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index 8191ee1..3d75c48 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -18,6 +18,7 @@
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.Display.INVALID_DISPLAY;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -82,12 +83,15 @@
@GuardedBy("ImfLock.class") @Nullable private IInputMethodInvoker mCurMethod;
@GuardedBy("ImfLock.class") private int mCurMethodUid = Process.INVALID_UID;
@GuardedBy("ImfLock.class") @Nullable private IBinder mCurToken;
- @GuardedBy("ImfLock.class") private int mCurTokenDisplayId = Display.INVALID_DISPLAY;
+ @GuardedBy("ImfLock.class") private int mCurTokenDisplayId = INVALID_DISPLAY;
@GuardedBy("ImfLock.class") private int mCurSeq;
@GuardedBy("ImfLock.class") private boolean mVisibleBound;
@GuardedBy("ImfLock.class") private boolean mSupportsStylusHw;
@GuardedBy("ImfLock.class") private boolean mSupportsConnectionlessStylusHw;
+ /** The display id for which the latest startInput was called. */
+ @GuardedBy("ImfLock.class") private int mDisplayIdToShowIme = INVALID_DISPLAY;
+
@Nullable private CountDownLatch mLatchForTesting;
/**
@@ -455,7 +459,7 @@
mWindowManagerInternal.removeWindowToken(mCurToken, true /* removeWindows */,
false /* animateExit */, mCurTokenDisplayId);
mCurToken = null;
- mCurTokenDisplayId = Display.INVALID_DISPLAY;
+ mCurTokenDisplayId = INVALID_DISPLAY;
}
@GuardedBy("ImfLock.class")
@@ -478,16 +482,15 @@
mCurId = info.getId();
mLastBindTime = SystemClock.uptimeMillis();
- final int displayIdToShowIme = mService.getDisplayIdToShowImeLocked();
mCurToken = new Binder();
- mCurTokenDisplayId = displayIdToShowIme;
+ mCurTokenDisplayId = mDisplayIdToShowIme;
if (DEBUG) {
Slog.v(TAG, "Adding window token: " + mCurToken + " for display: "
- + displayIdToShowIme);
+ + mDisplayIdToShowIme);
}
mWindowManagerInternal.addWindowToken(mCurToken,
WindowManager.LayoutParams.TYPE_INPUT_METHOD,
- displayIdToShowIme, null /* options */);
+ mDisplayIdToShowIme, null /* options */);
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
null, null, null, mCurId, mCurSeq, false);
@@ -596,4 +599,14 @@
unbindVisibleConnection();
}
}
+
+ @GuardedBy("ImfLock.class")
+ void setDisplayIdToShowIme(int displayId) {
+ mDisplayIdToShowIme = displayId;
+ }
+
+ @GuardedBy("ImfLock.class")
+ int getDisplayIdToShowIme() {
+ return mDisplayIdToShowIme;
+ }
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 8665a39..d236d7a 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -227,6 +227,16 @@
}
/**
+ * Indicates that the annotated field is shared by all the users.
+ *
+ * <p>See b/305849394 for details.</p>
+ */
+ @Retention(SOURCE)
+ @Target({ElementType.FIELD})
+ private @interface SharedByAllUsersField {
+ }
+
+ /**
* Indicates that the annotated field is not yet ready for concurrent multi-user support.
*
* <p>See b/305849394 for details.</p>
@@ -272,6 +282,7 @@
* {@link LayoutParams#SOFT_INPUT_STATE_ALWAYS_VISIBLE SOFT_INPUT_STATE_ALWAYS_VISIBLE}
* starting from {@link android.os.Build.VERSION_CODES#P}.
*/
+ @SharedByAllUsersField
private final boolean mPreventImeStartupUnlessTextEditor;
/**
@@ -279,6 +290,7 @@
* from the IME startup avoidance behavior that is enabled by
* {@link #mPreventImeStartupUnlessTextEditor}.
*/
+ @SharedByAllUsersField
@NonNull
private final String[] mNonPreemptibleInputMethods;
@@ -286,6 +298,7 @@
* See {@link #shouldEnableExperimentalConcurrentMultiUserMode(Context)} about when set to be
* {@code true}.
*/
+ @SharedByAllUsersField
private final boolean mExperimentalConcurrentMultiUserModeEnabled;
/**
@@ -327,6 +340,7 @@
final PackageManagerInternal mPackageManagerInternal;
final InputManagerInternal mInputManagerInternal;
final ImePlatformCompatUtils mImePlatformCompatUtils;
+ @SharedByAllUsersField
final InputMethodDeviceConfigs mInputMethodDeviceConfigs;
private final UserManagerInternal mUserManagerInternal;
@@ -339,6 +353,7 @@
private final ImeVisibilityStateComputer mVisibilityStateComputer;
@GuardedBy("ImfLock.class")
+ @SharedByAllUsersField
@NonNull
private final DefaultImeVisibilityApplier mVisibilityApplier;
@@ -355,7 +370,7 @@
// Mapping from deviceId to the device-specific imeId for that device.
@GuardedBy("ImfLock.class")
- @MultiUserUnawareField
+ @SharedByAllUsersField
private final SparseArray<String> mVirtualDeviceMethodMap = new SparseArray<>();
// TODO: Instantiate mSwitchingController for each user.
@@ -367,36 +382,19 @@
@MultiUserUnawareField
private HardwareKeyboardShortcutController mHardwareKeyboardShortcutController;
- /**
- * Tracks how many times {@link #mSettings} was updated.
- */
- @GuardedBy("ImfLock.class")
- private int mMethodMapUpdateCount = 0;
-
- /**
- * The display id for which the latest startInput was called.
- */
- @GuardedBy("ImfLock.class")
- int getDisplayIdToShowImeLocked() {
- return mDisplayIdToShowIme;
- }
-
- @GuardedBy("ImfLock.class")
- @MultiUserUnawareField
- private int mDisplayIdToShowIme = INVALID_DISPLAY;
-
@GuardedBy("ImfLock.class")
@MultiUserUnawareField
private int mDeviceIdToShowIme = DEVICE_ID_DEFAULT;
@Nullable
private StatusBarManagerInternal mStatusBarManagerInternal;
+ @SharedByAllUsersField
private boolean mShowOngoingImeSwitcherForPhones;
@GuardedBy("ImfLock.class")
@MultiUserUnawareField
private final HandwritingModeController mHwController;
@GuardedBy("ImfLock.class")
- @MultiUserUnawareField
+ @SharedByAllUsersField
private IntArray mStylusIds;
@GuardedBy("ImfLock.class")
@@ -475,6 +473,7 @@
/**
* Manages the IME clients.
*/
+ @SharedByAllUsersField
private final ClientController mClientController;
/**
@@ -486,6 +485,7 @@
/**
* Set once the system is ready to run third party code.
*/
+ @SharedByAllUsersField
boolean mSystemReady;
@GuardedBy("ImfLock.class")
@@ -522,6 +522,7 @@
/**
* The client that is currently bound to an input method.
*/
+ @MultiUserUnawareField
@Nullable
private ClientState mCurClient;
@@ -573,6 +574,7 @@
* {@link android.view.InsetsController} for the given window.
*/
@GuardedBy("ImfLock.class")
+ @SharedByAllUsersField
private final WeakHashMap<IBinder, Boolean> mFocusedWindowPerceptible = new WeakHashMap<>();
/**
@@ -677,28 +679,36 @@
@MultiUserUnawareField
int mImeWindowVis;
+ @SharedByAllUsersField
private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor();
+
+ @SharedByAllUsersField
private final String mSlotIme;
/**
* Registered {@link InputMethodListListener}.
* This variable can be accessed from both of MainThread and BinderThread.
*/
+ @SharedByAllUsersField
private final CopyOnWriteArrayList<InputMethodListListener> mInputMethodListListeners =
new CopyOnWriteArrayList<>();
@GuardedBy("ImfLock.class")
+ @SharedByAllUsersField
private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>();
@GuardedBy("ImfLock.class")
+ @SharedByAllUsersField
@NonNull
private final StartInputHistory mStartInputHistory = new StartInputHistory();
@GuardedBy("ImfLock.class")
+ @SharedByAllUsersField
@NonNull
private final SoftInputShowHideHistory mSoftInputShowHideHistory =
new SoftInputShowHideHistory();
+ @SharedByAllUsersField
@NonNull
private final ImeTrackerService mImeTrackerService;
@@ -1951,7 +1961,7 @@
final var statsToken = createStatsTokenForFocusedClient(false /* show */,
SoftInputShowHideReason.UNBIND_CURRENT_METHOD);
mVisibilityApplier.applyImeVisibility(mImeBindingState.mFocusedWindow, statsToken,
- STATE_HIDE_IME);
+ STATE_HIDE_IME, mCurrentUserId);
}
}
@@ -2122,7 +2132,8 @@
return InputBindResult.NOT_IME_TARGET_WINDOW;
}
final int csDisplayId = cs.mSelfReportedDisplayId;
- mDisplayIdToShowIme = mVisibilityStateComputer.computeImeDisplayId(winState, csDisplayId);
+ bindingController.setDisplayIdToShowIme(
+ mVisibilityStateComputer.computeImeDisplayId(winState, csDisplayId));
// Potentially override the selected input method if the new display belongs to a virtual
// device with a custom IME.
@@ -2193,8 +2204,9 @@
// We expect the caller has already verified that the client is allowed to access this
// display ID.
final String curId = bindingController.getCurId();
+ final int displayIdToShowIme = bindingController.getDisplayIdToShowIme();
if (curId != null && curId.equals(bindingController.getSelectedMethodId())
- && mDisplayIdToShowIme == getCurTokenDisplayIdLocked()) {
+ && displayIdToShowIme == getCurTokenDisplayIdLocked()) {
if (cs.mCurSession != null) {
// Fast case: if we are already connected to the input method,
// then just return it.
@@ -2245,7 +2257,9 @@
final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
final int oldDeviceId = mDeviceIdToShowIme;
- mDeviceIdToShowIme = mVdmInternal.getDeviceIdForDisplayId(mDisplayIdToShowIme);
+ final var bindingController = getInputMethodBindingController(userId);
+ final int displayIdToShowIme = bindingController.getDisplayIdToShowIme();
+ mDeviceIdToShowIme = mVdmInternal.getDeviceIdForDisplayId(displayIdToShowIme);
if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
if (oldDeviceId == DEVICE_ID_DEFAULT) {
return currentMethodId;
@@ -2279,7 +2293,7 @@
if (DEBUG) {
Slog.v(TAG, "Switching current input method from " + currentMethodId
+ " to device-specific one " + deviceMethodId + " because the current display "
- + mDisplayIdToShowIme + " belongs to device with id " + mDeviceIdToShowIme);
+ + displayIdToShowIme + " belongs to device with id " + mDeviceIdToShowIme);
}
return deviceMethodId;
}
@@ -2959,10 +2973,11 @@
@GuardedBy("ImfLock.class")
void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) {
- final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+ final int userId = mCurrentUserId;
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
if (enabledMayChange) {
final PackageManager userAwarePackageManager = getPackageManagerForUser(mContext,
- settings.getUserId());
+ userId);
List<InputMethodInfo> enabled = settings.getEnabledInputMethodList();
for (int i = 0; i < enabled.size(); i++) {
@@ -2991,20 +3006,19 @@
if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
String ime = SecureSettingsWrapper.getString(
- Settings.Secure.DEFAULT_INPUT_METHOD, null, settings.getUserId());
+ Settings.Secure.DEFAULT_INPUT_METHOD, null, userId);
String defaultDeviceIme = SecureSettingsWrapper.getString(
- Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, settings.getUserId());
+ Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, userId);
if (defaultDeviceIme != null && !Objects.equals(ime, defaultDeviceIme)) {
if (DEBUG) {
Slog.v(TAG, "Current input method " + ime + " differs from the stored default"
- + " device input method for user " + settings.getUserId()
+ + " device input method for user " + userId
+ " - restoring " + defaultDeviceIme);
}
SecureSettingsWrapper.putString(
- Settings.Secure.DEFAULT_INPUT_METHOD, defaultDeviceIme,
- settings.getUserId());
+ Settings.Secure.DEFAULT_INPUT_METHOD, defaultDeviceIme, userId);
SecureSettingsWrapper.putString(
- Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, settings.getUserId());
+ Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, userId);
}
}
@@ -3030,18 +3044,18 @@
}
// TODO: Instantiate mSwitchingController for each user.
- if (settings.getUserId() == mSwitchingController.getUserId()) {
+ if (userId == mSwitchingController.getUserId()) {
mSwitchingController.resetCircularListLocked(settings.getMethodMap());
} else {
mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
- mContext, settings.getMethodMap(), settings.getUserId());
+ mContext, settings.getMethodMap(), userId);
}
// TODO: Instantiate mHardwareKeyboardShortcutController for each user.
- if (settings.getUserId() == mHardwareKeyboardShortcutController.getUserId()) {
+ if (userId == mHardwareKeyboardShortcutController.getUserId()) {
mHardwareKeyboardShortcutController.reset(settings.getMethodMap());
} else {
mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController(
- settings.getMethodMap(), settings.getUserId());
+ settings.getMethodMap(), userId);
}
sendOnNavButtonFlagsChangedLocked();
}
@@ -3065,7 +3079,8 @@
@GuardedBy("ImfLock.class")
void setInputMethodLocked(String id, int subtypeId, int deviceId) {
- final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+ final int userId = mCurrentUserId;
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
InputMethodInfo info = settings.getMethodMap().get(id);
if (info == null) {
throw getExceptionForUnknownImeId(id);
@@ -3073,7 +3088,6 @@
// See if we need to notify a subtype change within the same IME.
if (id.equals(getSelectedMethodIdLocked())) {
- final int userId = settings.getUserId();
final int subtypeCount = info.getSubtypeCount();
if (subtypeCount <= 0) {
notifyInputMethodSubtypeChangedLocked(userId, info, null);
@@ -3129,7 +3143,7 @@
// mCurMethodId should be updated after setSelectedInputMethodAndSubtypeLocked()
// because mCurMethodId is stored as a history in
// setSelectedInputMethodAndSubtypeLocked().
- getInputMethodBindingController(mCurrentUserId).setSelectedMethodId(id);
+ getInputMethodBindingController(userId).setSelectedMethodId(id);
if (mActivityManagerInternal.isSystemReady()) {
Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED);
@@ -4653,7 +4667,7 @@
windowToken);
mVisibilityApplier.applyImeVisibility(requestToken, statsToken,
setVisible ? ImeVisibilityStateComputer.STATE_SHOW_IME
- : ImeVisibilityStateComputer.STATE_HIDE_IME);
+ : ImeVisibilityStateComputer.STATE_HIDE_IME, mCurrentUserId);
}
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -5226,7 +5240,6 @@
Slog.e(TAG, "buildInputMethodListLocked is not allowed until system is ready");
return;
}
- mMethodMapUpdateCount++;
final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
@@ -5446,7 +5459,8 @@
@GuardedBy("ImfLock.class")
private void resetSelectedInputMethodAndSubtypeLocked(String newDefaultIme) {
mDeviceIdToShowIme = DEVICE_ID_DEFAULT;
- mDisplayIdToShowIme = INVALID_DISPLAY;
+ final var bindingController = getInputMethodBindingController(mCurrentUserId);
+ bindingController.setDisplayIdToShowIme(INVALID_DISPLAY);
final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
settings.putSelectedDefaultDeviceInputMethod(null);
@@ -6051,7 +6065,7 @@
p.println("Current Input Method Manager state:");
final List<InputMethodInfo> methodList = settings.getMethodList();
int numImes = methodList.size();
- p.println(" Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount);
+ p.println(" Input Methods:");
for (int i = 0; i < numImes; i++) {
InputMethodInfo info = methodList.get(i);
p.println(" InputMethod #" + i + ":");
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index d5e85da..3673eb0 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -57,11 +57,8 @@
import java.util.Objects;
import java.util.Set;
-/**
- * Maintains a connection to a particular {@link MediaRoute2ProviderService}.
- */
-final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
- implements ServiceConnection {
+/** Maintains a connection to a particular {@link MediaRoute2ProviderService}. */
+final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
private static final String TAG = "MR2ProviderSvcProxy";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -69,6 +66,7 @@
private final int mUserId;
private final Handler mHandler;
private final boolean mIsSelfScanOnlyProvider;
+ private final ServiceConnection mServiceConnection = new ServiceConnectionImpl();
// Connection state
private boolean mRunning;
@@ -303,9 +301,12 @@
Intent service = new Intent(MediaRoute2ProviderService.SERVICE_INTERFACE);
service.setComponent(mComponentName);
try {
- mBound = mContext.bindServiceAsUser(service, this,
- Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
- new UserHandle(mUserId));
+ mBound =
+ mContext.bindServiceAsUser(
+ service,
+ mServiceConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+ new UserHandle(mUserId));
if (!mBound && DEBUG) {
Slog.d(TAG, this + ": Bind failed");
}
@@ -325,12 +326,11 @@
mBound = false;
disconnect();
- mContext.unbindService(this);
+ mContext.unbindService(mServiceConnection);
}
}
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
+ private void onServiceConnectedInternal(IBinder service) {
if (DEBUG) {
Slog.d(TAG, this + ": Connected");
}
@@ -354,16 +354,14 @@
}
}
- @Override
- public void onServiceDisconnected(ComponentName name) {
+ private void onServiceDisconnectedInternal() {
if (DEBUG) {
Slog.d(TAG, this + ": Service disconnected");
}
disconnect();
}
- @Override
- public void onBindingDied(ComponentName name) {
+ private void onBindingDiedInternal(ComponentName name) {
unbind();
if (Flags.enablePreventionOfKeepAliveRouteProviders()) {
Slog.w(
@@ -662,6 +660,37 @@
pendingTransferCount);
}
+ // All methods in this class are called on the main thread.
+ private final class ServiceConnectionImpl implements ServiceConnection {
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (Flags.enableMr2ServiceNonMainBgThread()) {
+ mHandler.post(() -> onServiceConnectedInternal(service));
+ } else {
+ onServiceConnectedInternal(service);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ if (Flags.enableMr2ServiceNonMainBgThread()) {
+ mHandler.post(() -> onServiceDisconnectedInternal());
+ } else {
+ onServiceDisconnectedInternal();
+ }
+ }
+
+ @Override
+ public void onBindingDied(ComponentName name) {
+ if (Flags.enableMr2ServiceNonMainBgThread()) {
+ mHandler.post(() -> onBindingDiedInternal(name));
+ } else {
+ onBindingDiedInternal(name);
+ }
+ }
+ }
+
private final class Connection implements DeathRecipient {
private final IMediaRoute2ProviderService mService;
private final ServiceCallbackStub mCallbackStub;
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 209cbb7..e34bdc6 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -728,7 +728,14 @@
final int compilationReason =
dexManager.getCompilationReasonForInstallScenario(
installRequest.getInstallScenario());
- return new DexoptOptions(packageName, compilationReason, dexoptFlags);
+ final AndroidPackage pkg = ps.getPkg();
+ var options = new DexoptOptions(packageName, compilationReason, dexoptFlags);
+ if (installRequest.getDexoptCompilerFilter() != null) {
+ options = options.overrideCompilerFilter(installRequest.getDexoptCompilerFilter());
+ } else if (pkg != null && pkg.isDebuggable()) {
+ options = options.overrideCompilerFilter(DexoptParams.COMPILER_FILTER_NOOP);
+ }
+ return options;
}
/**
@@ -772,12 +779,12 @@
&& installRequest.getInstallSource().mInitiatingPackageName.equals("android"))
: true;
+ // Don't skip the dexopt call if the compiler filter is "skip". Instead, call dexopt with
+ // the "skip" filter so that ART Service gets notified and skips dexopt itself.
return (!instantApp || Global.getInt(context.getContentResolver(),
Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
&& pkg != null
- && !pkg.isDebuggable()
&& (!onIncremental)
- && dexoptOptions.isCompilationEnabled()
&& !isApex
&& performDexOptForRollback;
}
diff --git a/services/core/java/com/android/server/pm/InstallArgs.java b/services/core/java/com/android/server/pm/InstallArgs.java
index 46f9732..8001615 100644
--- a/services/core/java/com/android/server/pm/InstallArgs.java
+++ b/services/core/java/com/android/server/pm/InstallArgs.java
@@ -58,6 +58,8 @@
final int mDataLoaderType;
final int mPackageSource;
final boolean mApplicationEnabledSettingPersistent;
+ @Nullable
+ final String mDexoptCompilerFilter;
// The list of instruction sets supported by this app. This is currently
// only used during the rmdex() phase to clean up resources. We can get rid of this
@@ -73,7 +75,7 @@
int autoRevokePermissionsMode, String traceMethod, int traceCookie,
SigningDetails signingDetails, int installReason, int installScenario,
boolean forceQueryableOverride, int dataLoaderType, int packageSource,
- boolean applicationEnabledSettingPersistent) {
+ boolean applicationEnabledSettingPersistent, String dexoptCompilerFilter) {
mOriginInfo = originInfo;
mMoveInfo = moveInfo;
mInstallFlags = installFlags;
@@ -96,5 +98,6 @@
mDataLoaderType = dataLoaderType;
mPackageSource = packageSource;
mApplicationEnabledSettingPersistent = applicationEnabledSettingPersistent;
+ mDexoptCompilerFilter = dexoptCompilerFilter;
}
}
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 8f51e36..dd2583a0d 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -174,13 +174,13 @@
mUserId = params.getUser().getIdentifier();
mInstallArgs = new InstallArgs(params.mOriginInfo, params.mMoveInfo, params.mObserver,
params.mInstallFlags, params.mDevelopmentInstallFlags, params.mInstallSource,
- params.mVolumeUuid, params.getUser(), null /*instructionSets*/,
+ params.mVolumeUuid, params.getUser(), null /*instructionSets*/,
params.mPackageAbiOverride, params.mPermissionStates,
params.mAllowlistedRestrictedPermissions, params.mAutoRevokePermissionsMode,
params.mTraceMethod, params.mTraceCookie, params.mSigningDetails,
params.mInstallReason, params.mInstallScenario, params.mForceQueryableOverride,
params.mDataLoaderType, params.mPackageSource,
- params.mApplicationEnabledSettingPersistent);
+ params.mApplicationEnabledSettingPersistent, params.mDexoptCompilerFilter);
mPackageLite = params.mPackageLite;
mPackageMetrics = new PackageMetrics(this);
mIsInstallInherit = params.mIsInherit;
@@ -709,6 +709,11 @@
return mWarnings;
}
+ @Nullable
+ public String getDexoptCompilerFilter() {
+ return mInstallArgs != null ? mInstallArgs.mDexoptCompilerFilter : null;
+ }
+
public void setScanFlags(int scanFlags) {
mScanFlags = scanFlags;
}
diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java
index d3a18f9..ccc1175 100644
--- a/services/core/java/com/android/server/pm/InstallingSession.java
+++ b/services/core/java/com/android/server/pm/InstallingSession.java
@@ -102,6 +102,7 @@
@Nullable
final DomainSet mPreVerifiedDomains;
final boolean mHasAppMetadataFile;
+ @Nullable final String mDexoptCompilerFilter;
// For move install
InstallingSession(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer,
@@ -136,6 +137,7 @@
mApplicationEnabledSettingPersistent = false;
mPreVerifiedDomains = null;
mHasAppMetadataFile = false;
+ mDexoptCompilerFilter = null;
}
InstallingSession(int sessionId, File stagedDir, IPackageInstallObserver2 observer,
@@ -172,6 +174,7 @@
mApplicationEnabledSettingPersistent = sessionParams.applicationEnabledSettingPersistent;
mPreVerifiedDomains = preVerifiedDomains;
mHasAppMetadataFile = hasAppMetadatafile;
+ mDexoptCompilerFilter = sessionParams.dexoptCompilerFilter;
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index e2ddba5..819a75c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -18,7 +18,7 @@
import android.os.SystemProperties;
-import com.android.server.pm.dex.DexoptOptions;
+import com.android.server.art.model.DexoptParams;
import dalvik.system.DexFile;
@@ -71,7 +71,7 @@
private static String getAndCheckValidity(int reason) {
String sysPropValue = SystemProperties.get(getSystemPropertyName(reason));
if (sysPropValue == null || sysPropValue.isEmpty()
- || !(sysPropValue.equals(DexoptOptions.COMPILER_FILTER_NOOP)
+ || !(sysPropValue.equals(DexoptParams.COMPILER_FILTER_NOOP)
|| DexFile.isValidCompilerFilter(sysPropValue))) {
throw new IllegalStateException("Value \"" + sysPropValue +"\" not valid "
+ "(reason " + REASON_STRINGS[reason] + ")");
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index a876616..7a53fe7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -121,6 +121,8 @@
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.android.server.art.ArtManagerLocal;
+import com.android.server.art.ReasonMapping;
+import com.android.server.art.model.DexoptParams;
import com.android.server.pm.PackageManagerShellCommandDataLoader.Metadata;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import com.android.server.pm.permission.PermissionAllowlist;
@@ -3589,6 +3591,14 @@
case "--package-source":
sessionParams.setPackageSource(Integer.parseInt(getNextArg()));
break;
+ case "--dexopt-compiler-filter":
+ sessionParams.dexoptCompilerFilter = getNextArgRequired();
+ // An early check that throws IllegalArgumentException if the compiler filter is
+ // invalid.
+ new DexoptParams.Builder(ReasonMapping.REASON_INSTALL)
+ .setCompilerFilter(sessionParams.dexoptCompilerFilter)
+ .build();
+ break;
default:
throw new IllegalArgumentException("Unknown option " + opt);
}
@@ -4735,6 +4745,7 @@
pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]");
pw.println(" [--apex] [--non-staged] [--force-non-staged]");
pw.println(" [--staged-ready-timeout TIMEOUT] [--ignore-dexopt-profile]");
+ pw.println(" [--dexopt-compiler-filter FILTER]");
pw.println(" [PATH [SPLIT...]|-]");
pw.println(" Install an application. Must provide the apk data to install, either as");
pw.println(" file path(s) or '-' to read from stdin. Options are:");
@@ -4781,13 +4792,19 @@
pw.println(" milliseconds for pre-reboot verification to complete when");
pw.println(" performing staged install. This flag is used to alter the waiting");
pw.println(" time. You can skip the waiting time by specifying a TIMEOUT of '0'");
- pw.println(" --ignore-dexopt-profile: If set, all profiles are ignored by dexopt");
+ pw.println(" --ignore-dexopt-profile: if set, all profiles are ignored by dexopt");
pw.println(" during the installation, including the profile in the DM file and");
pw.println(" the profile embedded in the APK file. If an invalid profile is");
pw.println(" provided during installation, no warning will be reported by `adb");
pw.println(" install`.");
pw.println(" This option does not affect later dexopt operations (e.g.,");
pw.println(" background dexopt and manual `pm compile` invocations).");
+ pw.println(" --dexopt-compiler-filter: the target compiler filter for dexopt during");
+ pw.println(" the installation. The filter actually used may be different.");
+ pw.println(" Valid values: one of the values documented in");
+ pw.println(" https://source.android.com/docs/core/runtime/configure"
+ + "#compiler_filters");
+ pw.println(" or 'skip'");
pw.println("");
pw.println(" install-existing [--user USER_ID|all|current]");
pw.println(" [--instant] [--full] [--wait] [--restrict-permissions] PACKAGE");
diff --git a/services/core/java/com/android/server/pm/PackageMetrics.java b/services/core/java/com/android/server/pm/PackageMetrics.java
index 2081f73..0acadb1 100644
--- a/services/core/java/com/android/server/pm/PackageMetrics.java
+++ b/services/core/java/com/android/server/pm/PackageMetrics.java
@@ -16,7 +16,12 @@
package com.android.server.pm;
+import static android.content.pm.PackageManager.GET_RESOLVED_FILTER;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
import static android.os.Process.INVALID_UID;
+import static android.os.Process.SYSTEM_UID;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -25,6 +30,7 @@
import android.app.ActivityManager;
import android.app.admin.SecurityLog;
import android.content.ComponentName;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.Flags;
import android.content.pm.PackageManager;
@@ -376,7 +382,30 @@
mCallingUid = callingUid;
}
- public boolean isSameComponent(ActivityInfo activityInfo) {
+ public boolean isLauncherActivity(@NonNull Computer computer, @UserIdInt int userId) {
+ if (mIsForWholeApp) {
+ return false;
+ }
+ // Query the launcher activities with the package name.
+ final Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_LAUNCHER);
+ intent.setPackage(mPackageName);
+ List<ResolveInfo> launcherActivities = computer.queryIntentActivitiesInternal(
+ intent, null,
+ MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | GET_RESOLVED_FILTER
+ | MATCH_DISABLED_COMPONENTS, SYSTEM_UID, userId);
+ final int launcherActivitiesSize =
+ launcherActivities != null ? launcherActivities.size() : 0;
+ for (int i = 0; i < launcherActivitiesSize; i++) {
+ ResolveInfo resolveInfo = launcherActivities.get(i);
+ if (isSameComponent(resolveInfo.activityInfo)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isSameComponent(ActivityInfo activityInfo) {
if (activityInfo == null) {
return false;
}
@@ -395,25 +424,13 @@
Slog.d(TAG, "Fail to report component state due to metrics is empty");
return;
}
- boolean isLauncher = false;
- final List<ResolveInfo> resolveInfosForLauncher = getHomeActivitiesResolveInfoAsUser(
- computer, userId);
- final int resolveInfosForLauncherSize =
- resolveInfosForLauncher != null ? resolveInfosForLauncher.size() : 0;
final int metricsSize = componentStateMetricsList.size();
for (int i = 0; i < metricsSize; i++) {
final ComponentStateMetrics componentStateMetrics = componentStateMetricsList.get(i);
- for (int j = 0; j < resolveInfosForLauncherSize; j++) {
- ResolveInfo resolveInfo = resolveInfosForLauncher.get(j);
- if (componentStateMetrics.isSameComponent(resolveInfo.activityInfo)) {
- isLauncher = true;
- break;
- }
- }
reportComponentStateChanged(componentStateMetrics.mUid,
componentStateMetrics.mComponentOldState,
componentStateMetrics.mComponentNewState,
- isLauncher,
+ componentStateMetrics.isLauncherActivity(computer, userId),
componentStateMetrics.mIsForWholeApp,
componentStateMetrics.mCallingUid);
}
@@ -424,10 +441,4 @@
FrameworkStatsLog.write(FrameworkStatsLog.COMPONENT_STATE_CHANGED_REPORTED,
uid, componentOldState, componentNewState, isLauncher, isForWholeApp, callingUid);
}
-
- private static List<ResolveInfo> getHomeActivitiesResolveInfoAsUser(@NonNull Computer computer,
- @UserIdInt int userId) {
- return computer.queryIntentActivitiesInternal(computer.getHomeIntent(), /* resolvedType */
- null, /* flags */ 0, userId);
- }
}
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 483d308..95e5b84 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -156,7 +156,8 @@
UserManager.DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO,
UserManager.DISALLOW_SIM_GLOBALLY,
UserManager.DISALLOW_ASSIST_CONTENT,
- UserManager.DISALLOW_THREAD_NETWORK
+ UserManager.DISALLOW_THREAD_NETWORK,
+ UserManager.DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO
});
public static final Set<String> DEPRECATED_USER_RESTRICTIONS = Sets.newArraySet(
@@ -208,7 +209,8 @@
UserManager.DISALLOW_CELLULAR_2G,
UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO,
UserManager.DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO,
- UserManager.DISALLOW_THREAD_NETWORK
+ UserManager.DISALLOW_THREAD_NETWORK,
+ UserManager.DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO
);
/**
@@ -254,7 +256,8 @@
UserManager.DISALLOW_CELLULAR_2G,
UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO,
UserManager.DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO,
- UserManager.DISALLOW_THREAD_NETWORK
+ UserManager.DISALLOW_THREAD_NETWORK,
+ UserManager.DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO
);
/**
diff --git a/services/core/java/com/android/server/pm/dex/DexoptOptions.java b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
index fcdc008..8cf248d 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptOptions.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
@@ -73,12 +73,6 @@
// or device setup and should be scheduled appropriately.
public static final int DEXOPT_FOR_RESTORE = 1 << 11; // TODO(b/135202722): remove
- /**
- * A value indicating that dexopt shouldn't be run. This string is only used when loading
- * filters from the `pm.dexopt.install*` properties and is not propagated to dex2oat.
- */
- public static final String COMPILER_FILTER_NOOP = "skip";
-
// The name of package to optimize.
private final String mPackageName;
@@ -186,10 +180,6 @@
return mCompilationReason;
}
- public boolean isCompilationEnabled() {
- return !mCompilerFilter.equals(COMPILER_FILTER_NOOP);
- }
-
/**
* Creates a new set of DexoptOptions which are the same with the exception of the compiler
* filter (set to the given value).
diff --git a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
index 3edd697..f518769 100644
--- a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
+++ b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
@@ -38,6 +38,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.function.DodecFunction;
+import com.android.internal.util.function.HexConsumer;
import com.android.internal.util.function.HexFunction;
import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadFunction;
@@ -269,8 +270,8 @@
if (isDelegatePermission(permissionName)) {
final long identity = Binder.clearCallingIdentity();
try {
- return checkPermission(SHELL_PKG, permissionName,
- persistentDeviceId, userId, superImpl);
+ return checkPermission(SHELL_PKG, permissionName, persistentDeviceId,
+ userId, superImpl);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -323,8 +324,7 @@
Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
try {
- return superImpl.apply(code, shellUid, "com.android.shell", null,
- virtualDeviceId, raw);
+ return superImpl.apply(code, shellUid, SHELL_PKG, null, virtualDeviceId, raw);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -340,7 +340,7 @@
Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
try {
- return superImpl.apply(code, usage, shellUid, "com.android.shell");
+ return superImpl.apply(code, usage, shellUid, SHELL_PKG);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -359,9 +359,8 @@
Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
try {
- return superImpl.apply(code, shellUid, "com.android.shell", featureId,
- virtualDeviceId, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage);
+ return superImpl.apply(code, shellUid, SHELL_PKG, featureId, virtualDeviceId,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -382,8 +381,8 @@
final long identity = Binder.clearCallingIdentity();
try {
return superImpl.apply(code,
- new AttributionSource(shellUid, Process.INVALID_PID,
- "com.android.shell", attributionSource.getAttributionTag(),
+ new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG,
+ attributionSource.getAttributionTag(),
attributionSource.getToken(), /*renouncedPermissions*/ null,
attributionSource.getDeviceId(), attributionSource.getNext()),
shouldCollectAsyncNotedOp, message, shouldCollectMessage,
@@ -409,10 +408,9 @@
Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
try {
- return superImpl.apply(token, code, shellUid, "com.android.shell",
- attributionTag, virtualDeviceId, startIfModeDefault,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage,
- attributionFlags, attributionChainId);
+ return superImpl.apply(token, code, shellUid, SHELL_PKG, attributionTag,
+ virtualDeviceId, startIfModeDefault, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage, attributionFlags, attributionChainId);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -438,8 +436,8 @@
final long identity = Binder.clearCallingIdentity();
try {
return superImpl.apply(clientId, code,
- new AttributionSource(shellUid, Process.INVALID_PID,
- "com.android.shell", attributionSource.getAttributionTag(),
+ new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG,
+ attributionSource.getAttributionTag(),
attributionSource.getToken(), /*renouncedPermissions*/ null,
attributionSource.getDeviceId(), attributionSource.getNext()),
startIfModeDefault, shouldCollectAsyncNotedOp, message,
@@ -465,11 +463,12 @@
final long identity = Binder.clearCallingIdentity();
try {
superImpl.apply(clientId, code,
- new AttributionSource(shellUid, Process.INVALID_PID,
- "com.android.shell", attributionSource.getAttributionTag(),
+ new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG,
+ attributionSource.getAttributionTag(),
attributionSource.getToken(), /*renouncedPermissions*/ null,
attributionSource.getDeviceId(), attributionSource.getNext()),
skipProxyOperation);
+ return;
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -477,6 +476,26 @@
superImpl.apply(clientId, code, attributionSource, skipProxyOperation);
}
+ @Override
+ public void finishOperation(IBinder clientId, int code, int uid, String packageName,
+ String attributionTag, int virtualDeviceId, @NonNull HexConsumer<IBinder, Integer,
+ Integer, String, String, Integer> superImpl) {
+ if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) {
+ final int shellUid =
+ UserHandle.getUid(UserHandle.getUserId(uid), Process.SHELL_UID);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ superImpl.accept(clientId, code, shellUid, SHELL_PKG, attributionTag,
+ virtualDeviceId);
+ return;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ superImpl.accept(clientId, code, uid, packageName, attributionTag,
+ virtualDeviceId);
+ }
+
private boolean isDelegatePermission(@NonNull String permission) {
// null permissions means all permissions are delegated
return mDelegateAndOwnerUid != INVALID_UID
diff --git a/services/core/java/com/android/server/powerstats/Android.bp b/services/core/java/com/android/server/powerstats/Android.bp
new file mode 100644
index 0000000..7f3b091
--- /dev/null
+++ b/services/core/java/com/android/server/powerstats/Android.bp
@@ -0,0 +1,11 @@
+aconfig_declarations {
+ name: "powerstats_flags",
+ package: "com.android.server.powerstats",
+ container: "system",
+ srcs: ["*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "powerstats_flags_lib",
+ aconfig_declarations: "powerstats_flags",
+}
diff --git a/services/core/java/com/android/server/powerstats/TimerTrigger.java b/services/core/java/com/android/server/powerstats/TimerTrigger.java
index f8a4135..817a40d 100644
--- a/services/core/java/com/android/server/powerstats/TimerTrigger.java
+++ b/services/core/java/com/android/server/powerstats/TimerTrigger.java
@@ -16,8 +16,10 @@
package com.android.server.powerstats;
+import android.app.AlarmManager;
import android.content.Context;
import android.os.Handler;
+import android.os.SystemClock;
import android.util.Slog;
/**
@@ -33,37 +35,53 @@
private static final long LOG_PERIOD_MS_HIGH_FREQUENCY = 2 * 60 * 1000; // 2 minutes
private final Handler mHandler;
+ private final AlarmManager mAlarmManager;
- private Runnable mLogDataLowFrequency = new Runnable() {
+ class PeriodicTimer implements Runnable, AlarmManager.OnAlarmListener {
+ private final String mName;
+ private final long mPeriodMs;
+ private final int mMsgType;
+
+ PeriodicTimer(String name, long periodMs, int msgType) {
+ mName = name;
+ mPeriodMs = periodMs;
+ mMsgType = msgType;
+ }
+
+ @Override
+ public void onAlarm() {
+ run();
+ }
+
@Override
public void run() {
- // Do not wake the device for these messages. Opportunistically log rail data every
- // LOG_PERIOD_MS_LOW_FREQUENCY.
- mHandler.postDelayed(mLogDataLowFrequency, LOG_PERIOD_MS_LOW_FREQUENCY);
- if (DEBUG) Slog.d(TAG, "Received delayed message. Log rail data low frequency");
- logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY);
+ if (Flags.alarmBasedPowerstatsLogging()) {
+ final long nextAlarmMs = SystemClock.elapsedRealtime() + mPeriodMs;
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, nextAlarmMs,
+ AlarmManager.WINDOW_EXACT, 0, mName, this, mHandler, null);
+ } else {
+ mHandler.postDelayed(this, mPeriodMs);
+ }
+ if (DEBUG) Slog.d(TAG, "Received delayed message (" + mName + "). Logging rail data");
+ logPowerStatsData(mMsgType);
}
- };
-
- private Runnable mLogDataHighFrequency = new Runnable() {
- @Override
- public void run() {
- // Do not wake the device for these messages. Opportunistically log rail data every
- // LOG_PERIOD_MS_HIGH_FREQUENCY.
- mHandler.postDelayed(mLogDataHighFrequency, LOG_PERIOD_MS_HIGH_FREQUENCY);
- if (DEBUG) Slog.d(TAG, "Received delayed message. Log rail data high frequency");
- logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY);
- }
- };
+ }
public TimerTrigger(Context context, PowerStatsLogger powerStatsLogger,
boolean triggerEnabled) {
super(context, powerStatsLogger);
mHandler = mContext.getMainThreadHandler();
+ mAlarmManager = mContext.getSystemService(AlarmManager.class);
if (triggerEnabled) {
- mLogDataLowFrequency.run();
- mLogDataHighFrequency.run();
+ final PeriodicTimer logDataLowFrequency = new PeriodicTimer("PowerStatsLowFreqLog",
+ LOG_PERIOD_MS_LOW_FREQUENCY,
+ PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY);
+ final PeriodicTimer logDataHighFrequency = new PeriodicTimer("PowerStatsHighFreqLog",
+ LOG_PERIOD_MS_HIGH_FREQUENCY,
+ PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY);
+ logDataLowFrequency.run();
+ logDataHighFrequency.run();
}
}
}
diff --git a/services/core/java/com/android/server/powerstats/flags.aconfig b/services/core/java/com/android/server/powerstats/flags.aconfig
new file mode 100644
index 0000000..0a4a751
--- /dev/null
+++ b/services/core/java/com/android/server/powerstats/flags.aconfig
@@ -0,0 +1,13 @@
+
+package: "com.android.server.powerstats"
+container: "system"
+
+flag {
+ name: "alarm_based_powerstats_logging"
+ namespace: "backstage_power"
+ description: "Utilize new OomAdjuster implementation"
+ bug: "294598168"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 04db3e8..3138a9e 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -1870,6 +1870,11 @@
@Override
public boolean isInSignificantPlace() {
+ if (android.security.Flags.significantPlaces()) {
+ mSignificantPlaceServiceWatcher.runOnBinder(
+ binder -> ISignificantPlaceProvider.Stub.asInterface(binder)
+ .onSignificantPlaceCheck());
+ }
return mIsInSignificantPlace;
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d9e14340..d38cd88 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -6548,7 +6548,10 @@
// Schedule an idle timeout in case the app doesn't do it for us.
mTaskSupervisor.scheduleIdleTimeout(this);
- mTaskSupervisor.reportResumedActivityLocked(this);
+ mTaskSupervisor.mStoppingActivities.remove(this);
+ if (getDisplayArea().allResumedActivitiesComplete()) {
+ mRootWindowContainer.executeAppTransitionForAllDisplay();
+ }
resumeKeyDispatchingLocked();
final Task rootTask = getRootTask();
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index d7a696f..e6d8132 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -139,6 +139,7 @@
import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
import com.android.server.wm.LaunchParamsController.LaunchParams;
import com.android.server.wm.TaskFragment.EmbeddingCheckResult;
+import com.android.wm.shell.Flags;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -1723,7 +1724,14 @@
// Get top task at beginning because the order may be changed when reusing existing task.
final Task prevTopRootTask = mPreferredTaskDisplayArea.getFocusedRootTask();
final Task prevTopTask = prevTopRootTask != null ? prevTopRootTask.getTopLeafTask() : null;
- final Task reusedTask = resolveReusableTask();
+ final boolean sourceActivityLaunchedFromBubble =
+ sourceRecord != null && sourceRecord.getLaunchedFromBubble();
+ // if the flag is enabled, allow reusing bubbled tasks only if the source activity is
+ // bubbled.
+ final boolean includeLaunchedFromBubble =
+ Flags.onlyReuseBubbledTaskWhenLaunchedFromBubble()
+ ? sourceActivityLaunchedFromBubble : true;
+ final Task reusedTask = resolveReusableTask(includeLaunchedFromBubble);
// If requested, freeze the task list
if (mOptions != null && mOptions.freezeRecentTasksReordering()
@@ -2722,8 +2730,11 @@
/**
* Decide whether the new activity should be inserted into an existing task. Returns null
* if not or an ActivityRecord with the task into which the new activity should be added.
+ *
+ * @param includeLaunchedFromBubble whether a task whose top activity was launched from a bubble
+ * should be allowed to be reused for the new activity.
*/
- private Task resolveReusableTask() {
+ private Task resolveReusableTask(boolean includeLaunchedFromBubble) {
// If a target task is specified, try to reuse that one
if (mOptions != null && mOptions.getLaunchTaskId() != INVALID_TASK_ID) {
Task launchTask = mRootWindowContainer.anyTaskForId(mOptions.getLaunchTaskId());
@@ -2767,7 +2778,8 @@
} else {
// Otherwise find the best task to put the activity in.
intentActivity =
- mRootWindowContainer.findTask(mStartActivity, mPreferredTaskDisplayArea);
+ mRootWindowContainer.findTask(mStartActivity, mPreferredTaskDisplayArea,
+ includeLaunchedFromBubble);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 3867d2d..b6e6991 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -2064,21 +2064,6 @@
}
}
- boolean reportResumedActivityLocked(ActivityRecord r) {
- // A resumed activity cannot be stopping. remove from list
- mStoppingActivities.remove(r);
-
- final Task rootTask = r.getRootTask();
- if (rootTask.getDisplayArea().allResumedActivitiesComplete()) {
- mRootWindowContainer.ensureActivitiesVisible();
- // Make sure activity & window visibility should be identical
- // for all displays in this stage.
- mRootWindowContainer.executeAppTransitionForAllDisplay();
- return true;
- }
- return false;
- }
-
// Called when WindowManager has finished animating the launchingBehind activity to the back.
private void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) {
final Task task = r.getTask();
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index f91ef1d..0febec9 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -200,6 +200,7 @@
}
infoBuilder.setOnBackInvokedCallback(callbackInfo.getCallback());
infoBuilder.setAnimationCallback(callbackInfo.isAnimationCallback());
+ infoBuilder.setTouchableRegion(window.getFrame());
mNavigationMonitor.startMonitor(window, navigationObserver);
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation currentTask=%s, "
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 0978cb4..a21ba26 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -77,12 +77,14 @@
mWindows.put(inputToken, window);
final InputTransferToken inputTransferToken = window.getInputTransferToken();
mWindowsByInputTransferToken.put(inputTransferToken, window);
- mWindowsByWindowToken.put(window.getWindowToken(), window);
+ final IBinder windowToken = window.getWindowToken();
+ mWindowsByWindowToken.put(windowToken, window);
updateProcessController(window);
window.mClient.linkToDeath(()-> {
synchronized (mGlobalLock) {
mWindows.remove(inputToken);
mWindowsByInputTransferToken.remove(inputTransferToken);
+ mWindowsByWindowToken.remove(windowToken);
}
}, 0);
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index c683d4d..f70d2a5 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -538,13 +538,6 @@
|| !mWindowManager.isKeyguardSecure(mService.getCurrentUserId());
}
- /**
- * @return Whether the dream activity is on top of default display.
- */
- boolean isShowingDream() {
- return getDisplayState(DEFAULT_DISPLAY).mShowingDream;
- }
-
private void updateKeyguardSleepToken() {
for (int displayNdx = mRootWindowContainer.getChildCount() - 1;
displayNdx >= 0; displayNdx--) {
@@ -631,7 +624,6 @@
* Note that this can be true even if the keyguard is disabled or not showing.
*/
private boolean mOccluded;
- private boolean mShowingDream;
private ActivityRecord mTopOccludesActivity;
private ActivityRecord mDismissingKeyguardActivity;
@@ -669,7 +661,6 @@
mRequestDismissKeyguard = false;
mOccluded = false;
- mShowingDream = false;
mTopOccludesActivity = null;
mDismissingKeyguardActivity = null;
@@ -697,21 +688,18 @@
// Only the top activity may control occluded, as we can't occlude the Keyguard
// if the top app doesn't want to occlude it.
- occludedByActivity = mTopOccludesActivity != null
+ mOccluded = mTopOccludesActivity != null
|| (mDismissingKeyguardActivity != null
&& task.topRunningActivity() == mDismissingKeyguardActivity
&& controller.canShowWhileOccluded(
true /* dismissKeyguard */, false /* showWhenLocked */));
// FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD only apply for secondary display.
if (mDisplayId != DEFAULT_DISPLAY) {
- occludedByActivity |= display.canShowWithInsecureKeyguard()
+ mOccluded |= display.canShowWithInsecureKeyguard()
&& controller.canDismissKeyguard();
}
}
- mShowingDream = display.getDisplayPolicy().isShowingDreamLw() && (top != null
- && top.getActivityType() == ACTIVITY_TYPE_DREAM);
- mOccluded = mShowingDream || occludedByActivity;
mRequestDismissKeyguard = lastDismissKeyguardActivity != mDismissingKeyguardActivity
&& !mOccluded && !mKeyguardGoingAway
&& mDismissingKeyguardActivity != null;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 9c7c41c..54ba47e 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -314,13 +314,19 @@
private boolean isDocument;
private Uri documentData;
- void init(int activityType, String taskAffinity, Intent intent, ActivityInfo info) {
+ // determines whether to include bubbled tasks. defaults to true to preserve previous
+ // behavior.
+ private boolean mIncludeLaunchedFromBubble = true;
+
+ void init(int activityType, String taskAffinity, Intent intent, ActivityInfo info,
+ boolean includeLaunchedFromBubble) {
mActivityType = activityType;
mTaskAffinity = taskAffinity;
mIntent = intent;
mInfo = info;
mIdealRecord = null;
mCandidateRecord = null;
+ mIncludeLaunchedFromBubble = includeLaunchedFromBubble;
}
/**
@@ -362,7 +368,8 @@
}
// Overlays should not be considered as the task's logical top activity.
- final ActivityRecord r = task.getTopNonFinishingActivity(false /* includeOverlays */);
+ final ActivityRecord r = task.getTopNonFinishingActivity(
+ false /* includeOverlays */, mIncludeLaunchedFromBubble);
if (r == null || r.finishing || r.mUserId != userId
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
@@ -1898,6 +1905,7 @@
// Don't do recursive work.
return;
}
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RWC_ensureActivitiesVisible");
mTaskSupervisor.beginActivityVisibilityUpdate();
try {
// First the front root tasks. In case any are not fullscreen and are in front of home.
@@ -1907,6 +1915,7 @@
}
} finally {
mTaskSupervisor.endActivityVisibilityUpdate();
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
@@ -2370,18 +2379,20 @@
}
@Nullable
- ActivityRecord findTask(ActivityRecord r, TaskDisplayArea preferredTaskDisplayArea) {
+ ActivityRecord findTask(ActivityRecord r, TaskDisplayArea preferredTaskDisplayArea,
+ boolean includeLaunchedFromBubble) {
return findTask(r.getActivityType(), r.taskAffinity, r.intent, r.info,
- preferredTaskDisplayArea);
+ preferredTaskDisplayArea, includeLaunchedFromBubble);
}
@Nullable
ActivityRecord findTask(int activityType, String taskAffinity, Intent intent, ActivityInfo info,
- TaskDisplayArea preferredTaskDisplayArea) {
+ TaskDisplayArea preferredTaskDisplayArea, boolean includeLaunchedFromBubble) {
ProtoLog.d(WM_DEBUG_TASKS, "Looking for task of type=%s, taskAffinity=%s, intent=%s"
- + ", info=%s, preferredTDA=%s", activityType, taskAffinity, intent, info,
- preferredTaskDisplayArea);
- mTmpFindTaskResult.init(activityType, taskAffinity, intent, info);
+ + ", info=%s, preferredTDA=%s, includeLaunchedFromBubble=%b", activityType,
+ taskAffinity, intent, info, preferredTaskDisplayArea, includeLaunchedFromBubble);
+ mTmpFindTaskResult.init(activityType, taskAffinity, intent, info,
+ includeLaunchedFromBubble);
// Looking up task on preferred display area first
ActivityRecord candidateActivity = null;
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 1cc1a57..7510180 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -157,7 +157,7 @@
// home & recent tasks
return;
}
- if (task.isVisible()) {
+ if (task.isVisibleRequested()) {
mTmpVisibleTasks.add(task);
} else {
mTmpInvisibleTasks.add(task);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index c02a437..22f718d 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4786,6 +4786,12 @@
if (com.android.window.flags.Flags.removePrepareSurfaceInPlacement()
&& lastParentBeforePip.mSyncState == SYNC_STATE_NONE) {
lastParentBeforePip.prepareSurfaces();
+ // If the moveToFront is a part of finishing transition, then make sure
+ // the z-order of tasks are up-to-date.
+ if (topActivity.mTransitionController.inFinishingTransition(topActivity)) {
+ Transition.assignLayers(taskDisplayArea,
+ taskDisplayArea.getPendingTransaction());
+ }
}
}
if (isPip2ExperimentEnabled) {
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 187b105..ab72e3c 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1104,21 +1104,34 @@
}
ActivityRecord getTopNonFinishingActivity() {
- return getTopNonFinishingActivity(true /* includeOverlays */);
+ return getTopNonFinishingActivity(
+ true /* includeOverlays */, true /* includeLaunchedFromBubble */);
}
/**
* Returns the top-most non-finishing activity, even if the activity is NOT ok to show to
* the current user.
* @param includeOverlays whether the task overlay activity should be included.
+ * @param includeLaunchedFromBubble whether activities that were launched from a bubble should
+ * be included.
* @see #topRunningActivity(boolean)
*/
- ActivityRecord getTopNonFinishingActivity(boolean includeOverlays) {
- // Split into 2 to avoid object creation due to variable capture.
+ ActivityRecord getTopNonFinishingActivity(boolean includeOverlays,
+ boolean includeLaunchedFromBubble) {
+ // Split to avoid object creation due to variable capture.
if (includeOverlays) {
- return getActivity((r) -> !r.finishing);
+ if (includeLaunchedFromBubble) {
+ return getActivity(r -> !r.finishing);
+ } else {
+ return getActivity(r -> !r.finishing && !r.getLaunchedFromBubble());
+ }
}
- return getActivity((r) -> !r.finishing && !r.isTaskOverlay());
+ if (includeLaunchedFromBubble) {
+ return getActivity(r -> !r.finishing && !r.isTaskOverlay());
+ } else {
+ return getActivity(
+ r -> !r.finishing && !r.isTaskOverlay() && !r.getLaunchedFromBubble());
+ }
}
ActivityRecord topRunningActivity() {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 4aa3e36..63ca469 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1048,7 +1048,7 @@
// the animation played. This puts the layers back into the correct order.
for (int i = displays.size() - 1; i >= 0; --i) {
if (displays.valueAt(i) == null) continue;
- updateDisplayLayers(displays.valueAt(i), t);
+ assignLayers(displays.valueAt(i), t);
}
for (int i = 0; i < info.getRootCount(); ++i) {
@@ -1056,12 +1056,13 @@
}
}
- private static void updateDisplayLayers(DisplayContent dc, SurfaceControl.Transaction t) {
- dc.mTransitionController.mBuildingFinishLayers = true;
+ /** Assigns the layers for the start or end state of transition. */
+ static void assignLayers(WindowContainer<?> wc, SurfaceControl.Transaction t) {
+ wc.mTransitionController.mBuildingFinishLayers = true;
try {
- dc.assignChildLayers(t);
+ wc.assignChildLayers(t);
} finally {
- dc.mTransitionController.mBuildingFinishLayers = false;
+ wc.mTransitionController.mBuildingFinishLayers = false;
}
}
@@ -2717,7 +2718,7 @@
rootLeash.setUnreleasedWarningCallSite("Transition.calculateTransitionRoots");
// Update layers to start transaction because we prevent assignment during collect, so
// the layer of transition root can be correct.
- updateDisplayLayers(dc, startT);
+ assignLayers(dc, startT);
startT.setLayer(rootLeash, leashReference.getLastLayer());
outInfo.addRootLeash(endDisplayId, rootLeash,
ancestor.getBounds().left, ancestor.getBounds().top);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8a6c73a..b814ccd 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3502,10 +3502,11 @@
if (!checkCallingPermission(permission.CONTROL_KEYGUARD, "dismissKeyguard")) {
throw new SecurityException("Requires CONTROL_KEYGUARD permission");
}
- if (!dreamHandlesConfirmKeys() && mAtmService.mKeyguardController.isShowingDream()) {
- mAtmService.mTaskSupervisor.wakeUp("leaveDream");
- }
synchronized (mGlobalLock) {
+ if (!dreamHandlesConfirmKeys()
+ && getDefaultDisplayContentLocked().getDisplayPolicy().isShowingDreamLw()) {
+ mAtmService.mTaskSupervisor.wakeUp("leaveDream");
+ }
mPolicy.dismissKeyguardLw(callback, message);
}
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index 221a991..a4ca317 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -68,12 +68,15 @@
public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTestBase {
private DefaultImeVisibilityApplier mVisibilityApplier;
+ private int mUserId = 0;
+
@Before
public void setUp() throws RemoteException {
super.setUp();
mVisibilityApplier =
(DefaultImeVisibilityApplier) mInputMethodManagerService.getVisibilityApplier();
synchronized (ImfLock.class) {
+ mUserId = mInputMethodManagerService.getCurrentImeUserIdLocked();
mInputMethodManagerService.setAttachedClientForTesting(requireNonNull(
mInputMethodManagerService.getClientStateLocked(mMockInputMethodClient)));
}
@@ -103,7 +106,7 @@
assertThrows(IllegalArgumentException.class, () -> {
synchronized (ImfLock.class) {
mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
- STATE_INVALID);
+ STATE_INVALID, mUserId);
}
});
}
@@ -112,7 +115,8 @@
public void testApplyImeVisibility_showIme() {
final var statsToken = ImeTracker.Token.empty();
synchronized (ImfLock.class) {
- mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_SHOW_IME);
+ mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_SHOW_IME,
+ mUserId);
}
verify(mMockWindowManagerInternal).showImePostLayout(eq(mWindowToken), eq(statsToken));
}
@@ -121,7 +125,8 @@
public void testApplyImeVisibility_hideIme() {
final var statsToken = ImeTracker.Token.empty();
synchronized (ImfLock.class) {
- mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME);
+ mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME,
+ mUserId);
}
verify(mMockWindowManagerInternal).hideIme(eq(mWindowToken), anyInt() /* displayId */,
eq(statsToken));
@@ -132,7 +137,7 @@
mInputMethodManagerService.mImeWindowVis = IME_ACTIVE;
synchronized (ImfLock.class) {
mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
- STATE_HIDE_IME_EXPLICIT);
+ STATE_HIDE_IME_EXPLICIT, mUserId);
}
verifyHideSoftInput(true, true);
}
@@ -142,7 +147,7 @@
mInputMethodManagerService.mImeWindowVis = IME_ACTIVE;
synchronized (ImfLock.class) {
mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
- STATE_HIDE_IME_NOT_ALWAYS);
+ STATE_HIDE_IME_NOT_ALWAYS, mUserId);
}
verifyHideSoftInput(true, true);
}
@@ -151,7 +156,7 @@
public void testApplyImeVisibility_showImeImplicit() throws Exception {
synchronized (ImfLock.class) {
mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
- STATE_SHOW_IME_IMPLICIT);
+ STATE_SHOW_IME_IMPLICIT, mUserId);
}
verifyShowSoftInput(true, true, 0 /* showFlags */);
}
@@ -166,10 +171,13 @@
final var statsToken = ImeTracker.Token.empty();
synchronized (ImfLock.class) {
- final int displayIdToShowIme = mInputMethodManagerService.getDisplayIdToShowImeLocked();
+ final var bindingController =
+ mInputMethodManagerService.getInputMethodBindingController(mUserId);
+ final int displayIdToShowIme = bindingController.getDisplayIdToShowIme();
// Verify hideIme will apply the expected displayId when the default IME
// visibility applier app STATE_HIDE_IME.
- mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME);
+ mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME,
+ mUserId);
verify(mInputMethodManagerService.mWindowManagerInternal).hideIme(
eq(mWindowToken), eq(displayIdToShowIme), eq(statsToken));
}
@@ -204,7 +212,9 @@
// Simulate the system hides the IME when switching IME services in different users.
// (e.g. unbinding the IME from the current user to the profile user)
final var statsToken = ImeTracker.Token.empty();
- final int displayIdToShowIme = mInputMethodManagerService.getDisplayIdToShowImeLocked();
+ final var bindingController =
+ mInputMethodManagerService.getInputMethodBindingController(mUserId);
+ final int displayIdToShowIme = bindingController.getDisplayIdToShowIme();
mInputMethodManagerService.hideCurrentInputLocked(mWindowToken,
statsToken, 0 /* flags */, null /* resultReceiver */,
HIDE_SWITCH_USER);
@@ -214,7 +224,7 @@
// the IME hidden state.
// The unbind will cancel the previous stats token, and create a new one internally.
verify(mVisibilityApplier).applyImeVisibility(
- eq(mWindowToken), any(), eq(STATE_HIDE_IME));
+ eq(mWindowToken), any(), eq(STATE_HIDE_IME), eq(mUserId) /* userId */);
verify(mInputMethodManagerService.mWindowManagerInternal).hideIme(
eq(mWindowToken), eq(displayIdToShowIme), and(not(eq(statsToken)), notNull()));
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceInfoTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceInfoTest.java
new file mode 100644
index 0000000..bacbf89
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceInfoTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import static com.android.server.display.DisplayDeviceInfo.DIFF_COLOR_MODE;
+import static com.android.server.display.DisplayDeviceInfo.DIFF_COMMITTED_STATE;
+import static com.android.server.display.DisplayDeviceInfo.DIFF_HDR_SDR_RATIO;
+import static com.android.server.display.DisplayDeviceInfo.DIFF_MODE_ID;
+import static com.android.server.display.DisplayDeviceInfo.DIFF_RENDER_TIMINGS;
+import static com.android.server.display.DisplayDeviceInfo.DIFF_ROTATION;
+import static com.android.server.display.DisplayDeviceInfo.DIFF_STATE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.view.Display;
+import android.view.Surface;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DisplayDeviceInfoTest {
+
+ @Test
+ public void testDiff_noChange() {
+ var oldDdi = createInfo();
+ var newDdi = createInfo();
+
+ assertThat(oldDdi.diff(newDdi)).isEqualTo(0);
+ }
+
+ @Test
+ public void testDiff_state() {
+ var oldDdi = createInfo();
+ var newDdi = createInfo();
+
+ newDdi.state = Display.STATE_VR;
+ assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_STATE);
+ }
+
+ @Test
+ public void testDiff_committedState() {
+ var oldDdi = createInfo();
+ var newDdi = createInfo();
+
+ newDdi.committedState = Display.STATE_UNKNOWN;
+ assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_COMMITTED_STATE);
+ }
+
+ @Test
+ public void testDiff_colorMode() {
+ var oldDdi = createInfo();
+ var newDdi = createInfo();
+
+ newDdi.colorMode = Display.COLOR_MODE_DISPLAY_P3;
+ assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_COLOR_MODE);
+ }
+
+ @Test
+ public void testDiff_hdrSdrRatio() {
+ var oldDdi = createInfo();
+ var newDdi = createInfo();
+
+ /* First change new ratio to non-NaN */
+ newDdi.hdrSdrRatio = 2.3f;
+ assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_HDR_SDR_RATIO);
+
+ /* Then change old to be non-NaN and also distinct */
+ oldDdi.hdrSdrRatio = 1.1f;
+ assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_HDR_SDR_RATIO);
+
+ /* Now make the new one NaN and the old one non-NaN */
+ newDdi.hdrSdrRatio = Float.NaN;
+ assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_HDR_SDR_RATIO);
+ }
+
+ @Test
+ public void testDiff_rotation() {
+ var oldDdi = createInfo();
+ var newDdi = createInfo();
+
+ newDdi.rotation = Surface.ROTATION_270;
+ assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_ROTATION);
+ }
+
+ @Test
+ public void testDiff_frameRate() {
+ var oldDdi = createInfo();
+ var newDdi = createInfo();
+
+ newDdi.renderFrameRate = 123.4f;
+ assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_RENDER_TIMINGS);
+ newDdi.renderFrameRate = oldDdi.renderFrameRate;
+
+ newDdi.appVsyncOffsetNanos = 31222221;
+ assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_RENDER_TIMINGS);
+ newDdi.appVsyncOffsetNanos = oldDdi.appVsyncOffsetNanos;
+
+ newDdi.presentationDeadlineNanos = 23000000;
+ assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_RENDER_TIMINGS);
+ }
+
+ @Test
+ public void testDiff_modeId() {
+ var oldDdi = createInfo();
+ var newDdi = createInfo();
+
+ newDdi.modeId = 9;
+ assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_MODE_ID);
+ }
+
+ private static DisplayDeviceInfo createInfo() {
+ var ddi = new DisplayDeviceInfo();
+ ddi.name = "TestDisplayDeviceInfo";
+ ddi.uniqueId = "test:51651561321";
+ ddi.width = 671;
+ ddi.height = 483;
+ ddi.modeId = 2;
+ ddi.renderFrameRate = 68.9f;
+ ddi.supportedModes = new Display.Mode[] {
+ new Display.Mode.Builder().setRefreshRate(68.9f).setResolution(671, 483).build(),
+ };
+ ddi.appVsyncOffsetNanos = 6233332;
+ ddi.presentationDeadlineNanos = 11500000;
+ ddi.rotation = Surface.ROTATION_90;
+ ddi.state = Display.STATE_ON;
+ ddi.committedState = Display.STATE_DOZE_SUSPEND;
+ return ddi;
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
index 8a33f34..1d04baa 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
@@ -570,6 +570,86 @@
assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
}
+ @Test
+ public void
+ updateBrightness_constructsDisplayBrightnessState_withNoAdjustmentFlag_isSlowChange() {
+ BrightnessEvent brightnessEvent = new BrightnessEvent(DISPLAY_ID);
+ mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(
+ mContext, DISPLAY_ID, displayId -> brightnessEvent, mDisplayManagerFlags);
+ mAutomaticBrightnessStrategy.setAutomaticBrightnessController(
+ mAutomaticBrightnessController);
+ float brightness = 0.4f;
+ BrightnessReason brightnessReason = new BrightnessReason();
+ brightnessReason.setReason(BrightnessReason.REASON_AUTOMATIC);
+ when(mAutomaticBrightnessController.getAutomaticScreenBrightness(brightnessEvent))
+ .thenReturn(brightness);
+
+ // Set the state such that auto-brightness was already applied
+ mAutomaticBrightnessStrategy.setAutoBrightnessApplied(true);
+
+ // Update the auto-brightess validity state to change the isSlowChange flag
+ mAutomaticBrightnessStrategy.isAutoBrightnessValid();
+
+ DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
+ mock(DisplayManagerInternal.DisplayPowerRequest.class);
+
+ DisplayBrightnessState expectedDisplayBrightnessState = new DisplayBrightnessState.Builder()
+ .setBrightness(brightness)
+ .setSdrBrightness(brightness)
+ .setBrightnessReason(brightnessReason)
+ .setDisplayBrightnessStrategyName(mAutomaticBrightnessStrategy.getName())
+ .setIsSlowChange(true)
+ .setBrightnessEvent(brightnessEvent)
+ .setBrightnessAdjustmentFlag(0)
+ .setShouldUpdateScreenBrightnessSetting(true)
+ .setIsUserInitiatedChange(true)
+ .build();
+ DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy
+ .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f,
+ /* userSetBrightnessChanged= */ true));
+ assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
+ }
+
+
+ @Test
+ public void updateBrightness_autoBrightnessNotApplied_noAdjustments_isNotSlowChange() {
+ BrightnessEvent brightnessEvent = new BrightnessEvent(DISPLAY_ID);
+ mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(
+ mContext, DISPLAY_ID, displayId -> brightnessEvent, mDisplayManagerFlags);
+ mAutomaticBrightnessStrategy.setAutomaticBrightnessController(
+ mAutomaticBrightnessController);
+ float brightness = 0.4f;
+ BrightnessReason brightnessReason = new BrightnessReason();
+ brightnessReason.setReason(BrightnessReason.REASON_AUTOMATIC);
+ when(mAutomaticBrightnessController.getAutomaticScreenBrightness(brightnessEvent))
+ .thenReturn(brightness);
+
+ // Set the state such that auto-brightness was not already applied
+ mAutomaticBrightnessStrategy.setAutoBrightnessApplied(false);
+
+ // Update the auto-brightess validity state to change the isSlowChange flag
+ mAutomaticBrightnessStrategy.isAutoBrightnessValid();
+
+ DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
+ mock(DisplayManagerInternal.DisplayPowerRequest.class);
+
+ DisplayBrightnessState expectedDisplayBrightnessState = new DisplayBrightnessState.Builder()
+ .setBrightness(brightness)
+ .setSdrBrightness(brightness)
+ .setBrightnessReason(brightnessReason)
+ .setDisplayBrightnessStrategyName(mAutomaticBrightnessStrategy.getName())
+ .setIsSlowChange(false)
+ .setBrightnessEvent(brightnessEvent)
+ .setBrightnessAdjustmentFlag(0)
+ .setShouldUpdateScreenBrightnessSetting(true)
+ .setIsUserInitiatedChange(true)
+ .build();
+ DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy
+ .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f,
+ /* userSetBrightnessChanged= */ true));
+ assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
+ }
+
private void setPendingAutoBrightnessAdjustment(float pendingAutoBrightnessAdjustment) {
Settings.System.putFloat(mContext.getContentResolver(),
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, pendingAutoBrightnessAdjustment);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 1fd8f50..ff1c6c8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -95,6 +95,8 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
import android.service.voice.IVoiceInteractionSession;
@@ -112,6 +114,7 @@
import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
import com.android.server.wm.utils.MockTracker;
+import com.android.wm.shell.Flags;
import org.junit.After;
import org.junit.Before;
@@ -492,7 +495,8 @@
// Start activity and delivered new intent.
starter.getIntent().setComponent(splitSecondReusableActivity.mActivityComponent);
- doReturn(splitSecondReusableActivity).when(mRootWindowContainer).findTask(any(), any());
+ doReturn(splitSecondReusableActivity)
+ .when(mRootWindowContainer).findTask(any(), any(), anyBoolean());
final int result = starter.setReason("testSplitScreenDeliverToTop").execute();
// Ensure result is delivering intent to top.
@@ -519,7 +523,8 @@
// Start activity and delivered new intent.
starter.getIntent().setComponent(splitSecondReusableActivity.mActivityComponent);
- doReturn(splitSecondReusableActivity).when(mRootWindowContainer).findTask(any(), any());
+ doReturn(splitSecondReusableActivity)
+ .when(mRootWindowContainer).findTask(any(), any(), anyBoolean());
final int result = starter.setReason("testSplitScreenMoveToFront").execute();
// Ensure result is moving task to front.
@@ -566,7 +571,7 @@
// Start activity and delivered new intent.
starter.getIntent().setComponent(activities.get(3).mActivityComponent);
- doReturn(activities.get(3)).when(mRootWindowContainer).findTask(any(), any());
+ doReturn(activities.get(3)).when(mRootWindowContainer).findTask(any(), any(), anyBoolean());
final int result = starter.setReason("testDesktopModeDeliverToTop").execute();
// Ensure result is delivering intent to top.
@@ -593,7 +598,8 @@
// Start activity and delivered new intent.
starter.getIntent().setComponent(desktopModeReusableActivity.mActivityComponent);
- doReturn(desktopModeReusableActivity).when(mRootWindowContainer).findTask(any(), any());
+ doReturn(desktopModeReusableActivity)
+ .when(mRootWindowContainer).findTask(any(), any(), anyBoolean());
final int result = starter.setReason("testDesktopModeMoveToFront").execute();
// Ensure result is moving task to front.
@@ -755,7 +761,7 @@
final ActivityRecord baseActivity = new ActivityBuilder(mAtm).setCreateTask(true).build();
baseActivity.getRootTask().setWindowingMode(WINDOWING_MODE_PINNED);
- doReturn(baseActivity).when(mRootWindowContainer).findTask(any(), any());
+ doReturn(baseActivity).when(mRootWindowContainer).findTask(any(), any(), anyBoolean());
ActivityOptions rawOptions = ActivityOptions.makeBasic()
.setPendingIntentCreatorBackgroundActivityStartMode(
@@ -1648,6 +1654,120 @@
assertNotEquals(inTask, target.getTask());
}
+ @EnableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE)
+ @Test
+ public void launchActivity_reusesBubbledTask() {
+ final ActivityStarter starter = prepareStarter(0, false);
+ final ActivityRecord bubbledActivity = createBubbledActivity();
+
+ // create the target activity to be launched with the same component as the bubbled activity
+ final ActivityRecord targetRecord = new ActivityBuilder(mAtm)
+ .setLaunchMode(LAUNCH_SINGLE_TASK)
+ .setComponent(ActivityBuilder.getDefaultComponent()).build();
+ starter.getIntent().setComponent(bubbledActivity.mActivityComponent);
+ startActivityInner(starter, targetRecord, bubbledActivity, null /* options */,
+ null /* inTask */, null /* inTaskFragment */);
+
+ assertEquals(bubbledActivity.getTask(), targetRecord.getTask());
+ }
+
+ @EnableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE)
+ @Test
+ public void launchActivity_nullSourceRecord_doesNotReuseBubbledTask() {
+ final ActivityStarter starter = prepareStarter(0, false);
+ final ActivityRecord bubbledActivity = createBubbledActivity();
+
+ // create the target activity to be launched
+ final ActivityRecord targetRecord =
+ new ActivityBuilder(mAtm)
+ .setLaunchMode(LAUNCH_SINGLE_TASK)
+ .setComponent(ActivityBuilder.getDefaultComponent()).build();
+ starter.getIntent().setComponent(bubbledActivity.mActivityComponent);
+
+ // pass null as the source record
+ startActivityInner(starter, targetRecord, null, null /* options */,
+ null /* inTask */, null /* inTaskFragment */);
+
+ assertNotEquals(bubbledActivity.getTask(), targetRecord.getTask());
+ }
+
+ @EnableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE)
+ @Test
+ public void launchActivity_nonBubbledSourceRecord_doesNotReuseBubbledTask() {
+ final ActivityStarter starter = prepareStarter(0, false);
+ final ActivityRecord bubbledActivity = createBubbledActivity();
+
+ // create a non bubbled activity
+ final ActivityRecord nonBubbleSourceRecord =
+ new ActivityBuilder(mAtm).setCreateTask(true)
+ .setLaunchMode(LAUNCH_SINGLE_TASK)
+ .setComponent(ActivityBuilder.getDefaultComponent())
+ .build();
+
+ // create the target activity to be launched
+ final ActivityRecord targetRecord =
+ new ActivityBuilder(mAtm)
+ .setLaunchMode(LAUNCH_SINGLE_TASK)
+ .setComponent(ActivityBuilder.getDefaultComponent()).build();
+ starter.getIntent().setComponent(bubbledActivity.mActivityComponent);
+
+ // use the non bubbled activity as the source
+ startActivityInner(starter, targetRecord, nonBubbleSourceRecord, null /* options */,
+ null /* inTask */, null /* inTaskFragment*/);
+
+ assertNotEquals(bubbledActivity.getTask(), targetRecord.getTask());
+ }
+
+ @DisableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE)
+ @Test
+ public void launchActivity_nullSourceRecord_flagDisabled_reusesBubbledTask() {
+ final ActivityStarter starter = prepareStarter(0, false);
+ final ActivityRecord bubbledActivity = createBubbledActivity();
+
+ // create the target activity to be launched
+ final ActivityRecord targetRecord =
+ new ActivityBuilder(mAtm)
+ .setLaunchMode(LAUNCH_SINGLE_TASK)
+ .setComponent(ActivityBuilder.getDefaultComponent()).build();
+ starter.getIntent().setComponent(bubbledActivity.mActivityComponent);
+
+ // pass null as the source record
+ startActivityInner(starter, targetRecord, null, null /* options */,
+ null /* inTask */, null /* inTaskFragment */);
+
+ assertEquals(bubbledActivity.getTask(), targetRecord.getTask());
+ }
+
+ @DisableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE)
+ @Test
+ public void launchActivity_fromBubble_flagDisabled_reusesBubbledTask() {
+ final ActivityStarter starter = prepareStarter(0, false);
+ final ActivityRecord bubbledActivity = createBubbledActivity();
+
+ // create the target activity to be launched with the same component as the bubbled activity
+ final ActivityRecord targetRecord =
+ new ActivityBuilder(mAtm)
+ .setLaunchMode(LAUNCH_SINGLE_TASK)
+ .setComponent(ActivityBuilder.getDefaultComponent()).build();
+ starter.getIntent().setComponent(bubbledActivity.mActivityComponent);
+ startActivityInner(starter, targetRecord, bubbledActivity, null /* options */,
+ null /* inTask */, null /* inTaskFragment */);
+
+ assertEquals(bubbledActivity.getTask(), targetRecord.getTask());
+ }
+
+ private ActivityRecord createBubbledActivity() {
+ final ActivityOptions opts = ActivityOptions.makeBasic();
+ opts.setTaskAlwaysOnTop(true);
+ opts.setLaunchedFromBubble(true);
+ opts.setLaunchBounds(new Rect(10, 10, 100, 100));
+ return new ActivityBuilder(mAtm)
+ .setCreateTask(true)
+ .setComponent(ActivityBuilder.getDefaultComponent())
+ .setActivityOptions(opts)
+ .build();
+ }
+
private static void startActivityInner(ActivityStarter starter, ActivityRecord target,
ActivityRecord source, ActivityOptions options, Task inTask,
TaskFragment inTaskFragment) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index ce90504..0f28528 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -405,11 +405,12 @@
final RootWindowContainer.FindTaskResult result =
new RootWindowContainer.FindTaskResult();
- result.init(r.getActivityType(), r.taskAffinity, r.intent, r.info);
+ result.init(r.getActivityType(), r.taskAffinity, r.intent, r.info, true);
result.process(task);
- assertEquals(r, task.getTopNonFinishingActivity(false /* includeOverlays */));
- assertEquals(taskOverlay, task.getTopNonFinishingActivity(true /* includeOverlays */));
+ assertEquals(r, task.getTopNonFinishingActivity(false /* includeOverlays */, true));
+ assertEquals(
+ taskOverlay, task.getTopNonFinishingActivity(true /* includeOverlays */, true));
assertNotNull(result.mIdealRecord);
}
@@ -432,7 +433,7 @@
final ActivityRecord r1 = new ActivityBuilder(mAtm).setComponent(
target).setTargetActivity(targetActivity).build();
RootWindowContainer.FindTaskResult result = new RootWindowContainer.FindTaskResult();
- result.init(r1.getActivityType(), r1.taskAffinity, r1.intent, r1.info);
+ result.init(r1.getActivityType(), r1.taskAffinity, r1.intent, r1.info, true);
result.process(parentTask);
assertThat(result.mIdealRecord).isNotNull();
@@ -440,7 +441,7 @@
final ActivityRecord r2 = new ActivityBuilder(mAtm).setComponent(
alias).setTargetActivity(targetActivity).build();
result = new RootWindowContainer.FindTaskResult();
- result.init(r2.getActivityType(), r2.taskAffinity, r2.intent, r2.info);
+ result.init(r2.getActivityType(), r2.taskAffinity, r2.intent, r2.info, true);
result.process(parentTask);
assertThat(result.mIdealRecord).isNotNull();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index d88871c..eb79118 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -147,6 +147,40 @@
}
@Test
+ public void testFindTask_includeLaunchedFromBubbled() {
+ final ComponentName component = ComponentName.createRelative(
+ DEFAULT_COMPONENT_PACKAGE_NAME, ".BubbledActivity");
+ final ActivityOptions opts = ActivityOptions.makeBasic();
+ opts.setTaskAlwaysOnTop(true);
+ opts.setLaunchedFromBubble(true);
+ final ActivityRecord activity = new ActivityBuilder(mWm.mAtmService)
+ .setComponent(component)
+ .setActivityOptions(opts)
+ .setCreateTask(true)
+ .build();
+
+ assertEquals(activity, mWm.mRoot.findTask(activity, activity.getTaskDisplayArea(),
+ true /* includeLaunchedFromBubble */));
+ }
+
+ @Test
+ public void testFindTask_ignoreLaunchedFromBubbled() {
+ final ComponentName component = ComponentName.createRelative(
+ DEFAULT_COMPONENT_PACKAGE_NAME, ".BubbledActivity");
+ final ActivityOptions opts = ActivityOptions.makeBasic();
+ opts.setTaskAlwaysOnTop(true);
+ opts.setLaunchedFromBubble(true);
+ final ActivityRecord activity = new ActivityBuilder(mWm.mAtmService)
+ .setComponent(component)
+ .setActivityOptions(opts)
+ .setCreateTask(true)
+ .build();
+
+ assertNull(mWm.mRoot.findTask(activity, activity.getTaskDisplayArea(),
+ false /* includeLaunchedFromBubble */));
+ }
+
+ @Test
public void testAllPausedActivitiesComplete() {
DisplayContent displayContent = mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY);
ActivityRecord activity = createActivityRecord(displayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index d57a7e6..f94e5e3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -56,6 +56,7 @@
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
+import android.app.ActivityOptions;
import android.content.pm.SigningDetails;
import android.content.res.Configuration;
import android.graphics.Color;
@@ -291,6 +292,30 @@
}
@Test
+ public void testFindTopNonFinishingActivity_ignoresLaunchedFromBubbleActivities() {
+ final ActivityOptions opts = ActivityOptions.makeBasic();
+ opts.setTaskAlwaysOnTop(true);
+ opts.setLaunchedFromBubble(true);
+ ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setUid(DEFAULT_TASK_FRAGMENT_ORGANIZER_UID).setActivityOptions(opts).build();
+ mTaskFragment.addChild(activity);
+
+ assertNull(mTaskFragment.getTopNonFinishingActivity(true, false));
+ }
+
+ @Test
+ public void testFindTopNonFinishingActivity_includesLaunchedFromBubbleActivities() {
+ final ActivityOptions opts = ActivityOptions.makeBasic();
+ opts.setTaskAlwaysOnTop(true);
+ opts.setLaunchedFromBubble(true);
+ ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setUid(DEFAULT_TASK_FRAGMENT_ORGANIZER_UID).setActivityOptions(opts).build();
+ mTaskFragment.addChild(activity);
+
+ assertEquals(mTaskFragment.getTopNonFinishingActivity(true, true), activity);
+ }
+
+ @Test
public void testMoveTaskToFront_supportsEnterPipOnTaskSwitchForAdjacentTaskFragment() {
final Task bottomTask = createTask(mDisplayContent);
final ActivityRecord bottomActivity = createActivityRecord(bottomTask);
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index a9ebd5c..2158f3d 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -252,4 +252,10 @@
/** The key to specify the emergency service category */
public static final String EXTRA_EMERGENCY_SERVICE_CATEGORY = "emergency_service_category";
+
+ /** The key to specify the alternate emergency URNs */
+ public static final String EXTRA_EMERGENCY_URNS = "emergency_urns";
+
+ /** The key to specify whether or not to use emergency routing */
+ public static final String EXTRA_USE_EMERGENCY_ROUTING = "use_emergency_routing";
}
diff --git a/tests/Internal/TEST_MAPPING b/tests/Internal/TEST_MAPPING
new file mode 100644
index 0000000..20af028
--- /dev/null
+++ b/tests/Internal/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "InternalTests"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/tools/localedata/extract_icu_data.py b/tools/localedata/extract_icu_data.py
index 81ac897..8f67fa8 100755
--- a/tools/localedata/extract_icu_data.py
+++ b/tools/localedata/extract_icu_data.py
@@ -22,6 +22,8 @@
import os.path
import sys
+import xml.etree.ElementTree as ElementTree
+
def get_locale_parts(locale):
"""Split a locale into three parts, for langauge, script, and region."""
@@ -40,42 +42,43 @@
def read_likely_subtags(input_file_name):
"""Read and parse ICU's likelySubtags.txt."""
- with open(input_file_name) as input_file:
- likely_script_dict = {
- # Android's additions for pseudo-locales. These internal codes make
- # sure that the pseudo-locales would not match other English or
- # Arabic locales. (We can't use private-use ISO 15924 codes, since
- # they may be used by apps for other purposes.)
- "en_XA": "~~~A",
- "ar_XB": "~~~B",
- # Removed data from later versions of ICU
- "ji": "Hebr", # Old code for Yiddish, still used in Java and Android
- }
- representative_locales = {
- # Android's additions
- "en_Latn_GB", # representative for en_Latn_001
- "es_Latn_MX", # representative for es_Latn_419
- "es_Latn_US", # representative for es_Latn_419 (not the best idea,
- # but Android has been shipping with it for quite a
- # while. Fortunately, MX < US, so if both exist, MX
- # would be chosen.)
- }
- for line in input_file:
- line = line.strip(u' \n\uFEFF')
- if line.startswith('//'):
- continue
- if '{' in line and '}' in line:
- from_locale = line[:line.index('{')]
- to_locale = line[line.index('"')+1:line.rindex('"')]
- from_lang, from_scr, from_region = get_locale_parts(from_locale)
- _, to_scr, to_region = get_locale_parts(to_locale)
- if from_lang == 'und':
- continue # not very useful for our purposes
- if from_region is None and to_region not in ['001', 'ZZ']:
- representative_locales.add(to_locale)
- if from_scr is None:
- likely_script_dict[from_locale] = to_scr
- return likely_script_dict, frozenset(representative_locales)
+ likely_script_dict = {
+ # Android's additions for pseudo-locales. These internal codes make
+ # sure that the pseudo-locales would not match other English or
+ # Arabic locales. (We can't use private-use ISO 15924 codes, since
+ # they may be used by apps for other purposes.)
+ "en_XA": "~~~A",
+ "ar_XB": "~~~B",
+ # Removed data from later versions of ICU
+ "ji": "Hebr", # Old code for Yiddish, still used in Java and Android
+ }
+ representative_locales = {
+ # Android's additions
+ "en_Latn_GB", # representative for en_Latn_001
+ "es_Latn_MX", # representative for es_Latn_419
+ "es_Latn_US", # representative for es_Latn_419 (not the best idea,
+ # but Android has been shipping with it for quite a
+ # while. Fortunately, MX < US, so if both exist, MX
+ # would be chosen.)
+ }
+ xml_tree = ElementTree.parse(input_file_name)
+ likely_subtags = xml_tree.find('likelySubtags')
+ for child in likely_subtags:
+ from_locale = child.get('from')
+ to_locale = child.get('to')
+ # print(f'from: {from_locale} to: {to_locale}')
+ from_lang, from_scr, from_region = get_locale_parts(from_locale)
+ _, to_scr, to_region = get_locale_parts(to_locale)
+ if to_locale == "FAIL":
+ continue # "FAIL" cases are not useful here.
+ if from_lang == 'und':
+ continue # not very useful for our purposes
+ if from_region is None and to_region not in ['001', 'ZZ']:
+ representative_locales.add(to_locale)
+ if from_scr is None:
+ likely_script_dict[from_locale] = to_scr
+
+ return likely_script_dict, frozenset(representative_locales)
# From packLanguageOrRegion() in ResourceTypes.cpp
@@ -86,7 +89,7 @@
elif len(inp) == 2:
return ord(inp[0]), ord(inp[1])
else:
- assert len(inp) == 3
+ assert len(inp) == 3, f'Expects a 3-character string, but "{inp}" '
base = ord(base)
first = ord(inp[0]) - base
second = ord(inp[1]) - base
@@ -161,9 +164,10 @@
print('});')
-def read_and_dump_likely_data(icu_data_dir):
+def read_and_dump_likely_data(cldr_source_dir):
"""Read and dump the likely-script data."""
- likely_subtags_txt = os.path.join(icu_data_dir, 'misc', 'likelySubtags.txt')
+ likely_subtags_txt = os.path.join(cldr_source_dir,
+ 'common', 'supplemental', 'likelySubtags.xml')
likely_script_dict, representative_locales = read_likely_subtags(
likely_subtags_txt)
@@ -280,10 +284,11 @@
icu_data_dir = os.path.join(
source_root,
'external', 'icu', 'icu4c', 'source', 'data')
+ cldr_source_dir = os.path.join(source_root, 'external', 'cldr')
print('// Auto-generated by %s' % sys.argv[0])
print()
- likely_script_dict = read_and_dump_likely_data(icu_data_dir)
+ likely_script_dict = read_and_dump_likely_data(cldr_source_dir)
read_and_dump_parent_data(icu_data_dir, likely_script_dict)