summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/NotificationChannelGroup.java21
-rw-r--r--core/java/android/hardware/biometrics/BiometricConstants.java5
-rw-r--r--core/java/android/hardware/biometrics/BiometricFaceConstants.java7
-rw-r--r--core/java/android/hardware/biometrics/BiometricFingerprintConstants.java9
-rw-r--r--core/java/android/hardware/biometrics/BiometricStateListener.java15
-rw-r--r--core/java/android/hardware/biometrics/IBiometricStateListener.aidl1
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java25
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl3
-rw-r--r--core/java/android/provider/Settings.java38
-rw-r--r--core/proto/android/server/powermanagerservice.proto1
-rw-r--r--core/res/res/values/config.xml16
-rw-r--r--core/res/res/values/strings.xml2
-rw-r--r--core/res/res/values/symbols.xml5
-rw-r--r--core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java13
-rw-r--r--graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java177
-rw-r--r--identity/java/android/security/identity/Util.java55
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java60
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java141
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java137
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java17
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java4
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java4
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java20
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java5
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt (renamed from packages/SystemUI/src/com/android/keyguard/FontInterpolator.kt)4
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt (renamed from packages/SystemUI/src/com/android/keyguard/TextAnimator.kt)8
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt (renamed from packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt)2
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml4
-rw-r--r--packages/SystemUI/res-keyguard/values/attrs.xml6
-rw-r--r--packages/SystemUI/res-keyguard/values/donottranslate.xml6
-rw-r--r--packages/SystemUI/res/layout/people_space_activity_no_conversations.xml1
-rw-r--r--packages/SystemUI/res/values/dimens.xml7
-rw-r--r--packages/SystemUI/shared/Android.bp1
-rw-r--r--packages/SystemUI/shared/res/values/attrs.xml28
-rw-r--r--packages/SystemUI/shared/res/values/donottranslate.xml23
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt (renamed from packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt)39
-rw-r--r--packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java32
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java1
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java11
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java10
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleViewModel.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt158
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesStartableModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt42
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt (renamed from packages/SystemUI/tests/src/com/android/keyguard/FontInterpolatorTest.kt)2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt (renamed from packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt)7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt (renamed from packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt)8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java71
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemBarAttributesListenerTest.kt250
-rw-r--r--services/core/java/com/android/server/am/AppRestrictionController.java33
-rw-r--r--services/core/java/com/android/server/audio/SpatializerHelper.java254
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallback.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/ClientMonitorCompositeCallback.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/PowerPressHandler.java27
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java199
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java23
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java10
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java3
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java5
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java50
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java7
-rw-r--r--services/core/java/com/android/server/policy/SideFpsEventHandler.java237
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java5
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java2
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java41
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java59
-rw-r--r--services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java124
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java46
92 files changed, 2087 insertions, 835 deletions
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
index 1769993e0e07..f97415ca20c8 100644
--- a/core/java/android/app/NotificationChannelGroup.java
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -20,7 +20,6 @@ import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
-import android.content.pm.ParceledListSlice;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -67,7 +66,7 @@ public final class NotificationChannelGroup implements Parcelable {
private CharSequence mName;
private String mDescription;
private boolean mBlocked;
- private ParceledListSlice<NotificationChannel> mChannels;
+ private List<NotificationChannel> mChannels = new ArrayList<>();
// Bitwise representation of fields that have been changed by the user
private int mUserLockedFields;
@@ -101,8 +100,7 @@ public final class NotificationChannelGroup implements Parcelable {
} else {
mDescription = null;
}
- mChannels = in.readParcelable(
- NotificationChannelGroup.class.getClassLoader(), ParceledListSlice.class);
+ in.readParcelableList(mChannels, NotificationChannel.class.getClassLoader(), android.app.NotificationChannel.class);
mBlocked = in.readBoolean();
mUserLockedFields = in.readInt();
}
@@ -129,7 +127,7 @@ public final class NotificationChannelGroup implements Parcelable {
} else {
dest.writeByte((byte) 0);
}
- dest.writeParcelable(mChannels, flags);
+ dest.writeParcelableList(mChannels, flags);
dest.writeBoolean(mBlocked);
dest.writeInt(mUserLockedFields);
}
@@ -159,7 +157,7 @@ public final class NotificationChannelGroup implements Parcelable {
* Returns the list of channels that belong to this group
*/
public List<NotificationChannel> getChannels() {
- return mChannels == null ? new ArrayList<>() : mChannels.getList();
+ return mChannels;
}
/**
@@ -193,8 +191,15 @@ public final class NotificationChannelGroup implements Parcelable {
/**
* @hide
*/
+ public void addChannel(NotificationChannel channel) {
+ mChannels.add(channel);
+ }
+
+ /**
+ * @hide
+ */
public void setChannels(List<NotificationChannel> channels) {
- mChannels = new ParceledListSlice<>(channels);
+ mChannels = channels;
}
/**
@@ -329,7 +334,7 @@ public final class NotificationChannelGroup implements Parcelable {
proto.write(NotificationChannelGroupProto.NAME, mName.toString());
proto.write(NotificationChannelGroupProto.DESCRIPTION, mDescription);
proto.write(NotificationChannelGroupProto.IS_BLOCKED, mBlocked);
- for (NotificationChannel channel : mChannels.getList()) {
+ for (NotificationChannel channel : mChannels) {
channel.dumpDebug(proto, NotificationChannelGroupProto.CHANNELS);
}
proto.end(token);
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index 99e4febe205d..943eee461809 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -157,6 +157,11 @@ public interface BiometricConstants {
int BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED = 18;
/**
+ * A power press stopped this biometric operation.
+ * @hide
+ */
+ int BIOMETRIC_ERROR_POWER_PRESSED = 19;
+ /**
* This constant is only used by SystemUI. It notifies SystemUI that authentication was paused
* because the authentication attempt was unsuccessful.
* @hide
diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
index fd46f243874b..2b62b98529a9 100644
--- a/core/java/android/hardware/biometrics/BiometricFaceConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
@@ -70,6 +70,7 @@ public interface BiometricFaceConstants {
BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
BIOMETRIC_ERROR_RE_ENROLL,
FACE_ERROR_UNKNOWN,
+ BIOMETRIC_ERROR_POWER_PRESSED,
})
@Retention(RetentionPolicy.SOURCE)
@interface FaceError {}
@@ -184,6 +185,12 @@ public interface BiometricFaceConstants {
int FACE_ERROR_UNKNOWN = 17;
/**
+ * A power press stopped this biometric operation.
+ * @hide
+ */
+ int BIOMETRIC_ERROR_POWER_PRESSED = 19;
+
+ /**
* Vendor codes received from the HAL start at 0. Codes that the framework exposes to keyguard
* append this value for some reason. We should probably remove this and just send the actual
* vendor code.
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
index d8ebb628452a..98f571b80949 100644
--- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -61,7 +61,8 @@ public interface BiometricFingerprintConstants {
BIOMETRIC_ERROR_RE_ENROLL,
BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
FINGERPRINT_ERROR_UNKNOWN,
- FINGERPRINT_ERROR_BAD_CALIBRATION})
+ FINGERPRINT_ERROR_BAD_CALIBRATION,
+ BIOMETRIC_ERROR_POWER_PRESSED})
@Retention(RetentionPolicy.SOURCE)
@interface FingerprintError {}
@@ -188,6 +189,12 @@ public interface BiometricFingerprintConstants {
int FINGERPRINT_ERROR_BAD_CALIBRATION = 18;
/**
+ * A power press stopped this biometric operation.
+ * @hide
+ */
+ int BIOMETRIC_ERROR_POWER_PRESSED = 19;
+
+ /**
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
diff --git a/core/java/android/hardware/biometrics/BiometricStateListener.java b/core/java/android/hardware/biometrics/BiometricStateListener.java
index 2ac0c1edaef0..b167cc6323d3 100644
--- a/core/java/android/hardware/biometrics/BiometricStateListener.java
+++ b/core/java/android/hardware/biometrics/BiometricStateListener.java
@@ -46,6 +46,14 @@ public abstract class BiometricStateListener extends IBiometricStateListener.Stu
public @interface State {
}
+ // The sensor received a touch.
+ public static final int ACTION_SENSOR_TOUCH = 0;
+
+ @IntDef({ACTION_SENSOR_TOUCH})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Action {
+ }
+
/**
* Defines behavior in response to state update
* @param newState new state of the biometric sensor
@@ -53,6 +61,13 @@ public abstract class BiometricStateListener extends IBiometricStateListener.Stu
public void onStateChanged(@BiometricStateListener.State int newState) {
}
+
+ /**
+ * Invoked when a biometric action has occurred.
+ */
+ public void onBiometricAction(@BiometricStateListener.Action int action) {
+ }
+
/**
* Invoked when enrollment state changes for the specified user
*/
diff --git a/core/java/android/hardware/biometrics/IBiometricStateListener.aidl b/core/java/android/hardware/biometrics/IBiometricStateListener.aidl
index 5bdced034017..6bb170d1cb62 100644
--- a/core/java/android/hardware/biometrics/IBiometricStateListener.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricStateListener.aidl
@@ -22,5 +22,6 @@ package android.hardware.biometrics;
*/
oneway interface IBiometricStateListener {
void onStateChanged(int newState);
+ void onBiometricAction(int action);
void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments);
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 28f1f02b7a93..c614cdbc3199 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -101,6 +101,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
private static final int MSG_FINGERPRINT_DETECTED = 107;
private static final int MSG_UDFPS_POINTER_DOWN = 108;
private static final int MSG_UDFPS_POINTER_UP = 109;
+ private static final int MSG_POWER_BUTTON_PRESSED = 110;
/**
* @hide
@@ -984,6 +985,16 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
/**
+ * This is triggered by SideFpsEventHandler
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void onPowerPressed() {
+ Slog.i(TAG, "onPowerPressed");
+ mHandler.obtainMessage(MSG_POWER_BUTTON_PRESSED).sendToTarget();
+ }
+
+ /**
* Determine if there is at least one fingerprint enrolled.
*
* @return true if at least one fingerprint is enrolled, false otherwise
@@ -1196,6 +1207,9 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
case MSG_UDFPS_POINTER_UP:
sendUdfpsPointerUp(msg.arg1 /* sensorId */);
break;
+ case MSG_POWER_BUTTON_PRESSED:
+ sendPowerPressed();
+ break;
default:
Slog.w(TAG, "Unknown message: " + msg.what);
@@ -1325,6 +1339,14 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
mAuthenticationCallback.onUdfpsPointerUp(sensorId);
}
+ private void sendPowerPressed() {
+ try {
+ mService.onPowerPressed();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending power press", e);
+ }
+ }
+
/**
* @hide
*/
@@ -1461,6 +1483,9 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
case FINGERPRINT_ERROR_BAD_CALIBRATION:
return context.getString(
com.android.internal.R.string.fingerprint_error_bad_calibration);
+ case BIOMETRIC_ERROR_POWER_PRESSED:
+ return context.getString(
+ com.android.internal.R.string.fingerprint_error_power_pressed);
case FINGERPRINT_ERROR_VENDOR: {
String[] msgArray = context.getResources().getStringArray(
com.android.internal.R.array.fingerprint_error_vendor);
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 0b6344617663..20cc58cc78a7 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -171,4 +171,7 @@ interface IFingerprintService {
// Registers BiometricStateListener.
void registerBiometricStateListener(IBiometricStateListener listener);
+
+ // Sends a power button pressed event to all listeners.
+ oneway void onPowerPressed();
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a63beecaaf91..ce354610433c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9704,6 +9704,43 @@ public final class Settings {
public static final String FACE_UNLOCK_RE_ENROLL = "face_unlock_re_enroll";
/**
+ * The time (in millis) to wait for a power button before sending a
+ * successful auth in to keyguard(for side fingerprint)
+ * @hide
+ */
+ @Readable
+ public static final String FINGERPRINT_SIDE_FPS_KG_POWER_WINDOW =
+ "fingerprint_side_fps_kg_power_window";
+
+ /**
+ * The time (in millis) to wait for a power button before sending
+ * a successful auth in biometric prompt(for side fingerprint)
+ * @hide
+ */
+ @Readable
+ public static final String FINGERPRINT_SIDE_FPS_BP_POWER_WINDOW =
+ "fingerprint_side_fps_bp_power_window";
+
+ /**
+ * The time (in millis) that a finger tap will wait for a power button
+ * before dismissing the power dialog during enrollment(for side
+ * fingerprint)
+ * @hide
+ */
+ @Readable
+ public static final String FINGERPRINT_SIDE_FPS_ENROLL_TAP_WINDOW =
+ "fingerprint_side_fps_enroll_tap_window";
+
+ /**
+ * The time (in millis) that a power event will ignore future authentications
+ * (for side fingerprint)
+ * @hide
+ */
+ @Readable
+ public static final String FINGERPRINT_SIDE_FPS_AUTH_DOWNTIME =
+ "fingerprint_side_fps_auth_downtime";
+
+ /**
* Whether or not debugging is enabled.
* @hide
*/
@@ -11309,6 +11346,7 @@ public final class Settings {
* <li>{@link BatteryManager#BATTERY_PLUGGED_AC} to stay on for AC charger</li>
* <li>{@link BatteryManager#BATTERY_PLUGGED_USB} to stay on for USB charger</li>
* <li>{@link BatteryManager#BATTERY_PLUGGED_WIRELESS} to stay on for wireless charger</li>
+ * <li>{@link BatteryManager#BATTERY_PLUGGED_DOCK} to stay on for dock charger</li>
* </ul>
* These values can be OR-ed together.
*/
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index 04f4d7b09d82..bd4f99044b17 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -238,6 +238,7 @@ message PowerServiceSettingsAndConfigurationDumpProto {
optional bool is_stay_on_while_plugged_in_ac = 1;
optional bool is_stay_on_while_plugged_in_usb = 2;
optional bool is_stay_on_while_plugged_in_wireless = 3;
+ optional bool is_stay_on_while_plugged_in_dock = 4;
}
message ScreenBrightnessSettingLimitsProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a8c7bf249ada..75034c7c2d2e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3498,6 +3498,22 @@
<!-- Specify if the fingerprint hardware support gestures-->
<bool name="config_fingerprintSupportsGestures">false</bool>
+ <!-- The time (in millis) to wait for a power button before sending
+ a successful auth in biometric prompt(for side fingerprint) -->
+ <integer name="config_sidefpsBpPowerPressWindow">300</integer>
+
+ <!-- The time (in millis) to wait for a power button before sending a
+ successful auth in to keyguard(for side fingerprint) -->
+ <integer name="config_sidefpsKeyguardPowerPressWindow">300</integer>
+
+ <!-- The time (in millis) that a power event will ignore future authentications
+ (for side fingerprint) -->
+ <integer name="config_sidefpsPostAuthDowntime">400</integer>
+
+ <!-- The time (in millis) that a finger tap will wait for a power button
+ before dismissing the power dialog during enrollment(for side fingerprint) -->
+ <integer name="config_sidefpsEnrollPowerPressWindow">300</integer>
+
<!-- This config is used to force VoiceInteractionService to start on certain low ram devices.
It declares the package name of VoiceInteractionService that should be started. -->
<string translatable="false" name="config_forceVoiceInteractionServicePackage"></string>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 3787ff99383f..d5283852c295 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1751,6 +1751,8 @@
<string name="fingerprint_error_security_update_required">Sensor temporarily disabled.</string>
<!-- Generic error message shown when fingerprint needs calibration [CHAR LIMIT=150] -->
<string name="fingerprint_error_bad_calibration">Can\u2019t use fingerprint sensor. Visit a repair provider</string>
+ <!-- Generic error message shown when the power button has been pressed. [CHAR LIMIT=150] -->
+ <string name="fingerprint_error_power_pressed">Power button pressed</string>
<!-- Template to be used to name enrolled fingerprints by default. -->
<string name="fingerprint_name_template">Finger <xliff:g id="fingerId" example="1">%d</xliff:g></string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4a8b3a75b6ee..b2fd28fc27e6 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2622,10 +2622,15 @@
<java-symbol type="string" name="fingerprint_recalibrate_notification_name" />
<java-symbol type="string" name="fingerprint_recalibrate_notification_title" />
<java-symbol type="string" name="fingerprint_recalibrate_notification_content" />
+ <java-symbol type="string" name="fingerprint_error_power_pressed" />
<!-- Fingerprint config -->
<java-symbol type="integer" name="config_fingerprintMaxTemplatesPerUser"/>
<java-symbol type="bool" name="config_fingerprintSupportsGestures"/>
+ <java-symbol type="integer" name="config_sidefpsBpPowerPressWindow"/>
+ <java-symbol type="integer" name="config_sidefpsKeyguardPowerPressWindow"/>
+ <java-symbol type="integer" name="config_sidefpsPostAuthDowntime"/>
+ <java-symbol type="integer" name="config_sidefpsEnrollPowerPressWindow"/>
<!-- Face authentication messages -->
<java-symbol type="string" name="face_recalibrate_notification_name" />
diff --git a/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java b/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java
index 7338c3a3ea8a..ddc05e09c395 100644
--- a/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java
+++ b/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java
@@ -204,6 +204,19 @@ public class AdaptiveIconDrawableTest extends AndroidTestCase {
assertEquals(100, Color.alpha(bitmap.getPixel(50, 50)));
}
+ @Test
+ public void testSetBounds() throws Exception {
+ mIconDrawable = new AdaptiveIconDrawable(mBackgroundDrawable, mForegroundDrawable);
+ mIconDrawable.setBounds(0, 0, 100, 100);
+
+ assertEquals(new Rect(-25, -25, 125, 125), mBackgroundDrawable.getBounds());
+ assertEquals(new Rect(-25, -25, 125, 125), mForegroundDrawable.getBounds());
+
+ mIconDrawable.setBounds(10, 10, 110, 110);
+ assertEquals(new Rect(-15, -15, 135, 135), mBackgroundDrawable.getBounds());
+ assertEquals(new Rect(-15, -15, 135, 135), mForegroundDrawable.getBounds());
+ }
+
//
// Utils
//
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index 2f56b181f455..6939a72019e8 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -26,8 +26,6 @@ import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
import android.graphics.BlendMode;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -39,8 +37,6 @@ import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Region;
-import android.graphics.Shader;
-import android.graphics.Shader.TileMode;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.PathParser;
@@ -106,7 +102,8 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
private static final float DEFAULT_VIEW_PORT_SCALE = 1f / (1 + 2 * EXTRA_INSET_PERCENTAGE);
/**
- * Clip path defined in R.string.config_icon_mask.
+ * Unused path.
+ * TODO: Remove once the layoutLib is updated
*/
private static Path sMask;
@@ -114,9 +111,10 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
* Scaled mask based on the view bounds.
*/
private final Path mMask;
- private final Path mMaskScaleOnly;
+ private final Path mMaskTransformed;
private final Matrix mMaskMatrix;
private final Region mTransparentRegion;
+ private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
/**
* Indices used to access {@link #mLayerState.mChildDrawable} array for foreground and
@@ -131,19 +129,10 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
*/
LayerState mLayerState;
- private Shader mLayersShader;
- private Bitmap mLayersBitmap;
-
private final Rect mTmpOutRect = new Rect();
private Rect mHotspotBounds;
private boolean mMutated;
- private boolean mSuspendChildInvalidation;
- private boolean mChildRequestedInvalidation;
- private final Canvas mCanvas;
- private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG |
- Paint.FILTER_BITMAP_FLAG);
-
/**
* Constructor used for xml inflation.
*/
@@ -157,19 +146,16 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
*/
AdaptiveIconDrawable(@Nullable LayerState state, @Nullable Resources res) {
mLayerState = createConstantState(state, res);
- // config_icon_mask from context bound resource may have been chaged using
+ // config_icon_mask from context bound resource may have been changed using
// OverlayManager. Read that one first.
Resources r = ActivityThread.currentActivityThread() == null
? Resources.getSystem()
: ActivityThread.currentActivityThread().getApplication().getResources();
- // TODO: either make sMask update only when config_icon_mask changes OR
- // get rid of it all-together in layoutlib
- sMask = PathParser.createPathFromPathData(r.getString(R.string.config_icon_mask));
- mMask = new Path(sMask);
- mMaskScaleOnly = new Path(mMask);
+ mMask = PathParser.createPathFromPathData(r.getString(R.string.config_icon_mask));
+ mMaskTransformed = new Path();
mMaskMatrix = new Matrix();
- mCanvas = new Canvas();
mTransparentRegion = new Region();
+ mPaint.setColor(Color.BLACK);
}
private ChildDrawable createChildDrawable(Drawable drawable) {
@@ -280,7 +266,7 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
* @return the mask path object used to clip the drawable
*/
public Path getIconMask() {
- return mMask;
+ return mMaskTransformed;
}
/**
@@ -322,92 +308,47 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
if (bounds.isEmpty()) {
return;
}
- updateLayerBounds(bounds);
- }
-
- private void updateLayerBounds(Rect bounds) {
- if (bounds.isEmpty()) {
- return;
- }
- try {
- suspendChildInvalidation();
- updateLayerBoundsInternal(bounds);
- updateMaskBoundsInternal(bounds);
- } finally {
- resumeChildInvalidation();
- }
- }
-
- /**
- * Set the child layer bounds bigger than the view port size by {@link #DEFAULT_VIEW_PORT_SCALE}
- */
- private void updateLayerBoundsInternal(Rect bounds) {
- int cX = bounds.width() / 2;
- int cY = bounds.height() / 2;
+ // Set the child layer bounds bigger than the view port size
+ // by {@link #DEFAULT_VIEW_PORT_SCALE}
+ float cX = bounds.exactCenterX();
+ float cY = bounds.exactCenterY();
+ float insetWidth = bounds.width() / (DEFAULT_VIEW_PORT_SCALE * 2);
+ float insetHeight = bounds.height() / (DEFAULT_VIEW_PORT_SCALE * 2);
+ final Rect outRect = mTmpOutRect;
+ outRect.set(
+ (int) (cX - insetWidth),
+ (int) (cY - insetHeight),
+ (int) (cX + insetWidth),
+ (int) (cY + insetHeight));
for (int i = 0, count = mLayerState.N_CHILDREN; i < count; i++) {
final ChildDrawable r = mLayerState.mChildren[i];
- final Drawable d = r.mDrawable;
- if (d == null) {
- continue;
+ if (r.mDrawable != null) {
+ r.mDrawable.setBounds(outRect);
}
-
- int insetWidth = (int) (bounds.width() / (DEFAULT_VIEW_PORT_SCALE * 2));
- int insetHeight = (int) (bounds.height() / (DEFAULT_VIEW_PORT_SCALE * 2));
- final Rect outRect = mTmpOutRect;
- outRect.set(cX - insetWidth, cY - insetHeight, cX + insetWidth, cY + insetHeight);
-
- d.setBounds(outRect);
}
- }
-
- private void updateMaskBoundsInternal(Rect b) {
- // reset everything that depends on the view bounds
- mMaskMatrix.setScale(b.width() / MASK_SIZE, b.height() / MASK_SIZE);
- sMask.transform(mMaskMatrix, mMaskScaleOnly);
- mMaskMatrix.postTranslate(b.left, b.top);
- sMask.transform(mMaskMatrix, mMask);
+ // Update the clipping mask
+ mMaskMatrix.setScale(bounds.width() / MASK_SIZE, bounds.height() / MASK_SIZE);
+ mMaskMatrix.postTranslate(bounds.left, bounds.top);
+ mMask.transform(mMaskMatrix, mMaskTransformed);
- if (mLayersBitmap == null || mLayersBitmap.getWidth() != b.width()
- || mLayersBitmap.getHeight() != b.height()) {
- mLayersBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ARGB_8888);
- }
-
- mPaint.setShader(null);
+ // Clear the transparent region, it is calculated lazily
mTransparentRegion.setEmpty();
- mLayersShader = null;
}
@Override
public void draw(Canvas canvas) {
- if (mLayersBitmap == null) {
- return;
+ int saveCount = canvas.save();
+ canvas.clipPath(mMaskTransformed);
+ canvas.drawPaint(mPaint);
+ if (mLayerState.mChildren[BACKGROUND_ID].mDrawable != null) {
+ mLayerState.mChildren[BACKGROUND_ID].mDrawable.draw(canvas);
}
- if (mLayersShader == null) {
- mCanvas.setBitmap(mLayersBitmap);
- mCanvas.drawColor(Color.BLACK);
- if (mLayerState.mChildren[BACKGROUND_ID].mDrawable != null) {
- mLayerState.mChildren[BACKGROUND_ID].mDrawable.draw(mCanvas);
- }
- if (mLayerState.mChildren[FOREGROUND_ID].mDrawable != null) {
- mLayerState.mChildren[FOREGROUND_ID].mDrawable.draw(mCanvas);
- }
- mLayersShader = new BitmapShader(mLayersBitmap, TileMode.CLAMP, TileMode.CLAMP);
- mPaint.setShader(mLayersShader);
- }
- if (mMaskScaleOnly != null) {
- Rect bounds = getBounds();
- canvas.translate(bounds.left, bounds.top);
- canvas.drawPath(mMaskScaleOnly, mPaint);
- canvas.translate(-bounds.left, -bounds.top);
+ if (mLayerState.mChildren[FOREGROUND_ID].mDrawable != null) {
+ mLayerState.mChildren[FOREGROUND_ID].mDrawable.draw(canvas);
}
- }
-
- @Override
- public void invalidateSelf() {
- mLayersShader = null;
- super.invalidateSelf();
+ canvas.restoreToCount(saveCount);
}
@Override
@@ -600,37 +541,9 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
return false;
}
- /**
- * Temporarily suspends child invalidation.
- *
- * @see #resumeChildInvalidation()
- */
- private void suspendChildInvalidation() {
- mSuspendChildInvalidation = true;
- }
-
- /**
- * Resumes child invalidation after suspension, immediately performing an
- * invalidation if one was requested by a child during suspension.
- *
- * @see #suspendChildInvalidation()
- */
- private void resumeChildInvalidation() {
- mSuspendChildInvalidation = false;
-
- if (mChildRequestedInvalidation) {
- mChildRequestedInvalidation = false;
- invalidateSelf();
- }
- }
-
@Override
public void invalidateDrawable(@NonNull Drawable who) {
- if (mSuspendChildInvalidation) {
- mChildRequestedInvalidation = true;
- } else {
- invalidateSelf();
- }
+ invalidateSelf();
}
@Override
@@ -714,6 +627,13 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
+ final ChildDrawable[] array = mLayerState.mChildren;
+ for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+ final Drawable dr = array[i].mDrawable;
+ if (dr != null) {
+ dr.setAlpha(alpha);
+ }
+ }
}
@Override
@@ -816,10 +736,6 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
}
}
- if (changed) {
- updateLayerBounds(getBounds());
- }
-
return changed;
}
@@ -835,10 +751,6 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
}
}
- if (changed) {
- updateLayerBounds(getBounds());
- }
-
return changed;
}
@@ -979,6 +891,7 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
int mDensity;
// The density to use when inflating/looking up the children drawables. A value of 0 means
+
// use the system's density.
int mSrcDensityOverride = 0;
diff --git a/identity/java/android/security/identity/Util.java b/identity/java/android/security/identity/Util.java
index e56bd5167906..789ff06f064b 100644
--- a/identity/java/android/security/identity/Util.java
+++ b/identity/java/android/security/identity/Util.java
@@ -20,12 +20,12 @@ import android.annotation.NonNull;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECPoint;
-import java.util.Collection;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
@@ -36,15 +36,6 @@ import javax.crypto.spec.SecretKeySpec;
public class Util {
private static final String TAG = "Util";
- static int[] integerCollectionToArray(Collection<Integer> collection) {
- int[] result = new int[collection.size()];
- int n = 0;
- for (int item : collection) {
- result[n++] = item;
- }
- return result;
- }
-
static byte[] stripLeadingZeroes(byte[] value) {
int n = 0;
while (n < value.length && value[n] == 0) {
@@ -61,15 +52,47 @@ public class Util {
static byte[] publicKeyEncodeUncompressedForm(PublicKey publicKey) {
ECPoint w = ((ECPublicKey) publicKey).getW();
- // X and Y are always positive so for interop we remove any leading zeroes
- // inserted by the BigInteger encoder.
- byte[] x = stripLeadingZeroes(w.getAffineX().toByteArray());
- byte[] y = stripLeadingZeroes(w.getAffineY().toByteArray());
+ BigInteger x = w.getAffineX();
+ BigInteger y = w.getAffineY();
+ if (x.compareTo(BigInteger.ZERO) < 0) {
+ throw new RuntimeException("X is negative");
+ }
+ if (y.compareTo(BigInteger.ZERO) < 0) {
+ throw new RuntimeException("Y is negative");
+ }
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(0x04);
- baos.write(x);
- baos.write(y);
+
+ // Each coordinate may be encoded in 33*, 32, or fewer bytes.
+ //
+ // * : it can be 33 bytes because toByteArray() guarantees "The array will contain the
+ // minimum number of bytes required to represent this BigInteger, including at
+ // least one sign bit, which is (ceil((this.bitLength() + 1)/8))" which means that
+ // the MSB is always 0x00. This is taken care of by calling calling
+ // stripLeadingZeroes().
+ //
+ // We need the encoding to be exactly 32 bytes since according to RFC 5480 section 2.2
+ // and SEC 1: Elliptic Curve Cryptography section 2.3.3 the encoding is 0x04 | X | Y
+ // where X and Y are encoded in exactly 32 byte, big endian integer values each.
+ //
+ byte[] xBytes = stripLeadingZeroes(x.toByteArray());
+ if (xBytes.length > 32) {
+ throw new RuntimeException("xBytes is " + xBytes.length + " which is unexpected");
+ }
+ for (int n = 0; n < 32 - xBytes.length; n++) {
+ baos.write(0x00);
+ }
+ baos.write(xBytes);
+
+ byte[] yBytes = stripLeadingZeroes(y.toByteArray());
+ if (yBytes.length > 32) {
+ throw new RuntimeException("yBytes is " + yBytes.length + " which is unexpected");
+ }
+ for (int n = 0; n < 32 - yBytes.length; n++) {
+ baos.write(0x00);
+ }
+ baos.write(yBytes);
return baos.toByteArray();
} catch (IOException e) {
throw new RuntimeException("Unexpected IOException", e);
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 b3de18308c1f..1155ea174ed1 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
@@ -62,6 +62,7 @@ import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.util.Log;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -930,6 +931,12 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
/** Called when exiting PIP transition is finished to do the state cleanup. */
void onExitPipFinished(TaskInfo info) {
+ if (mLeash == null) {
+ // TODO(239461594): Remove once the double call to onExitPipFinished() is fixed
+ Log.w(TAG, "Warning, onExitPipFinished() called multiple times in the same sessino");
+ return;
+ }
+
clearWaitForFixedRotation();
if (mSwipePipToHomeOverlay != null) {
removeContentOverlay(mSwipePipToHomeOverlay, null /* callback */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 53ec39d954c4..63f55c49851b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -352,41 +352,31 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent,
@SplitPosition int position, @Nullable Bundle options) {
+ if (fillInIntent == null) {
+ fillInIntent = new Intent();
+ }
+ // Flag this as a no-user-action launch to prevent sending user leaving event to the
+ // current top activity since it's going to be put into another side of the split. This
+ // prevents the current top activity from going into pip mode due to user leaving event.
+ fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
+
+ // Flag with MULTIPLE_TASK if this is launching the same activity into both sides of the
+ // split.
+ if (isLaunchingAdjacently(intent.getIntent(), position)) {
+ fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
+ }
+
if (!ENABLE_SHELL_TRANSITIONS) {
startIntentLegacy(intent, fillInIntent, position, options);
return;
}
- try {
- options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
- null /* wct */);
-
- if (fillInIntent == null) {
- fillInIntent = new Intent();
- }
- // Flag this as a no-user-action launch to prevent sending user leaving event to the
- // current top activity since it's going to be put into another side of the split. This
- // prevents the current top activity from going into pip mode due to user leaving event.
- fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
-
- // Flag with MULTIPLE_TASK if this is launching the same activity into both sides of the
- // split.
- if (isLaunchingAdjacently(intent.getIntent(), position)) {
- fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
- }
-
- intent.send(mContext, 0, fillInIntent, null /* onFinished */, null /* handler */,
- null /* requiredPermission */, options);
- } catch (PendingIntent.CanceledException e) {
- Slog.e(TAG, "Failed to launch task", e);
- }
+ mStageCoordinator.startIntent(intent, fillInIntent, position, options);
}
- private void startIntentLegacy(PendingIntent intent, @Nullable Intent fillInIntent,
+ private void startIntentLegacy(PendingIntent intent, Intent fillInIntent,
@SplitPosition int position, @Nullable Bundle options) {
- boolean startSameActivityAdjacently = isLaunchingAdjacently(intent.getIntent(), position);
-
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
mStageCoordinator.prepareEvictChildTasks(position, evictWct);
@@ -397,8 +387,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
IRemoteAnimationFinishedCallback finishedCallback,
SurfaceControl.Transaction t) {
if (apps == null || apps.length == 0) {
- if (startSameActivityAdjacently) {
- // Switch split position if dragging the same activity to another side.
+ // Switch the split position if launching as MULTIPLE_TASK failed.
+ if ((fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
setSideStagePosition(SplitLayout.reversePosition(
mStageCoordinator.getSideStagePosition()));
}
@@ -432,18 +422,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
final WindowContainerTransaction wct = new WindowContainerTransaction();
options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, wct);
- // Flag this as a no-user-action launch to prevent sending user leaving event to the current
- // top activity since it's going to be put into another side of the split. This prevents the
- // current top activity from going into pip mode due to user leaving event.
- if (fillInIntent == null) {
- fillInIntent = new Intent();
- }
- fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
- if (startSameActivityAdjacently) {
- fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
- }
-
wct.sendPendingIntent(intent, fillInIntent, options);
mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 056cd5813861..83bdf8bfb727 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -62,13 +62,12 @@ class SplitScreenTransitions {
private final Runnable mOnFinish;
DismissTransition mPendingDismiss = null;
- IBinder mPendingEnter = null;
- IBinder mPendingRecent = null;
+ TransitSession mPendingEnter = null;
+ TransitSession mPendingRecent = null;
private IBinder mAnimatingTransition = null;
OneShotRemoteHandler mPendingRemoteHandler = null;
private OneShotRemoteHandler mActiveRemoteHandler = null;
- private boolean mEnterTransitionMerged;
private final Transitions.TransitionFinishCallback mRemoteFinishCB = this::onFinish;
@@ -145,7 +144,7 @@ class SplitScreenTransitions {
continue;
}
- if (transition == mPendingEnter && (mainRoot.equals(change.getContainer())
+ if (isPendingEnter(transition) && (mainRoot.equals(change.getContainer())
|| sideRoot.equals(change.getContainer()))) {
t.setPosition(leash, change.getEndAbsBounds().left, change.getEndAbsBounds().top);
t.setWindowCrop(leash, change.getEndAbsBounds().width(),
@@ -171,12 +170,40 @@ class SplitScreenTransitions {
onFinish(null /* wct */, null /* wctCB */);
}
+ boolean isPendingTransition(IBinder transition) {
+ return isPendingEnter(transition)
+ || isPendingDismiss(transition)
+ || isPendingRecent(transition);
+ }
+
+ boolean isPendingEnter(IBinder transition) {
+ return mPendingEnter != null && mPendingEnter.mTransition == transition;
+ }
+
+ boolean isPendingRecent(IBinder transition) {
+ return mPendingRecent != null && mPendingRecent.mTransition == transition;
+ }
+
+ boolean isPendingDismiss(IBinder transition) {
+ return mPendingDismiss != null && mPendingDismiss.mTransition == transition;
+ }
+
/** Starts a transition to enter split with a remote transition animator. */
- IBinder startEnterTransition(@WindowManager.TransitionType int transitType,
- @NonNull WindowContainerTransaction wct, @Nullable RemoteTransition remoteTransition,
- @NonNull Transitions.TransitionHandler handler) {
+ IBinder startEnterTransition(
+ @WindowManager.TransitionType int transitType,
+ WindowContainerTransaction wct,
+ @Nullable RemoteTransition remoteTransition,
+ Transitions.TransitionHandler handler,
+ @Nullable TransitionCallback callback) {
final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
- mPendingEnter = transition;
+ setEnterTransition(transition, remoteTransition, callback);
+ return transition;
+ }
+
+ /** Sets a transition to enter split. */
+ void setEnterTransition(@NonNull IBinder transition,
+ @Nullable RemoteTransition remoteTransition, @Nullable TransitionCallback callback) {
+ mPendingEnter = new TransitSession(transition, callback);
if (remoteTransition != null) {
// Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
@@ -184,7 +211,9 @@ class SplitScreenTransitions {
mTransitions.getMainExecutor(), remoteTransition);
mPendingRemoteHandler.setTransition(transition);
}
- return transition;
+
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ + " deduced Enter split screen");
}
/** Starts a transition to dismiss split. */
@@ -209,8 +238,8 @@ class SplitScreenTransitions {
}
void setRecentTransition(@NonNull IBinder transition,
- @Nullable RemoteTransition remoteTransition) {
- mPendingRecent = transition;
+ @Nullable RemoteTransition remoteTransition, @Nullable TransitionCallback callback) {
+ mPendingRecent = new TransitSession(transition, callback);
if (remoteTransition != null) {
// Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
@@ -226,6 +255,18 @@ class SplitScreenTransitions {
void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
IBinder mergeTarget, Transitions.TransitionFinishCallback finishCallback) {
if (mergeTarget != mAnimatingTransition) return;
+
+ if (isPendingEnter(transition) && isPendingRecent(mergeTarget)) {
+ mPendingRecent.mCallback = new TransitionCallback() {
+ @Override
+ public void onTransitionFinished(WindowContainerTransaction finishWct,
+ SurfaceControl.Transaction finishT) {
+ // Since there's an entering transition merged, recent transition no longer
+ // need to handle entering split screen after the transition finished.
+ }
+ };
+ }
+
if (mActiveRemoteHandler != null) {
mActiveRemoteHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
} else {
@@ -247,38 +288,55 @@ class SplitScreenTransitions {
}
void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) {
- if (aborted) return;
+ if (isPendingEnter(transition)) {
+ if (!aborted) {
+ // An enter transition got merged, appends the rest operations to finish entering
+ // split screen.
+ // TODO (b/238856352): Passed-in the proper finish transition to merge instead.
+ if (mFinishTransaction == null) {
+ mFinishTransaction = mTransactionPool.acquire();
+ }
+ mStageCoordinator.finishEnterSplitScreen(mFinishTransaction);
+ }
- // Once a pending enter transition got merged, make sure to append the reset of finishing
- // operations to the finish transition.
- if (transition == mPendingEnter) {
- mFinishTransaction = mTransactionPool.acquire();
- mStageCoordinator.finishEnterSplitScreen(mFinishTransaction);
+ mPendingEnter.mCallback.onTransitionConsumed(aborted);
mPendingEnter = null;
mPendingRemoteHandler = null;
- mEnterTransitionMerged = true;
+ } else if (isPendingDismiss(transition)) {
+ mPendingDismiss.mCallback.onTransitionConsumed(aborted);
+ mPendingDismiss = null;
+ } else if (isPendingRecent(transition)) {
+ mPendingRecent.mCallback.onTransitionConsumed(aborted);
+ mPendingRecent = null;
+ mPendingRemoteHandler = null;
}
}
void onFinish(WindowContainerTransaction wct, WindowContainerTransactionCallback wctCB) {
if (!mAnimations.isEmpty()) return;
- if (mAnimatingTransition == mPendingEnter) {
+
+ TransitionCallback callback = null;
+ if (isPendingEnter(mAnimatingTransition)) {
+ callback = mPendingEnter.mCallback;
mPendingEnter = null;
}
- if (mPendingDismiss != null && mPendingDismiss.mTransition == mAnimatingTransition) {
+ if (isPendingDismiss(mAnimatingTransition)) {
+ callback = mPendingDismiss.mCallback;
mPendingDismiss = null;
}
- if (mAnimatingTransition == mPendingRecent) {
- if (!mEnterTransitionMerged) {
- if (wct == null) wct = new WindowContainerTransaction();
- mStageCoordinator.onRecentTransitionFinished(wct, mFinishTransaction);
- }
+ if (isPendingRecent(mAnimatingTransition)) {
+ callback = mPendingRecent.mCallback;
mPendingRecent = null;
}
+
+ if (callback != null) {
+ if (wct == null) wct = new WindowContainerTransaction();
+ callback.onTransitionFinished(wct, mFinishTransaction);
+ }
+
mPendingRemoteHandler = null;
mActiveRemoteHandler = null;
mAnimatingTransition = null;
- mEnterTransitionMerged = false;
mOnFinish.run();
if (mFinishTransaction != null) {
@@ -382,17 +440,34 @@ class SplitScreenTransitions {
|| info.getType() == TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
}
- /** Bundled information of dismiss transition. */
- static class DismissTransition {
- IBinder mTransition;
+ /** Clean-up callbacks for transition. */
+ interface TransitionCallback {
+ /** Calls when the transition got consumed. */
+ default void onTransitionConsumed(boolean aborted) {}
+
+ /** Calls when the transition finished. */
+ default void onTransitionFinished(WindowContainerTransaction finishWct,
+ SurfaceControl.Transaction finishT) {}
+ }
- int mReason;
+ /** Session for a transition and its clean-up callback. */
+ static class TransitSession {
+ final IBinder mTransition;
+ TransitionCallback mCallback;
- @SplitScreen.StageType
- int mDismissTop;
+ TransitSession(IBinder transition, @Nullable TransitionCallback callback) {
+ mTransition = transition;
+ mCallback = callback != null ? callback : new TransitionCallback() {};
+ }
+ }
+
+ /** Bundled information of dismiss transition. */
+ static class DismissTransition extends TransitSession {
+ final int mReason;
+ final @SplitScreen.StageType int mDismissTop;
DismissTransition(IBinder transition, int reason, int dismissTop) {
- this.mTransition = transition;
+ super(transition, null /* callback */);
this.mReason = reason;
this.mDismissTop = dismissTop;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 2229e26b9343..b33a2829bee4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -24,6 +24,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.TRANSIT_CHANGE;
@@ -214,6 +215,33 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
};
+ private final SplitScreenTransitions.TransitionCallback mRecentTransitionCallback =
+ new SplitScreenTransitions.TransitionCallback() {
+ @Override
+ public void onTransitionFinished(WindowContainerTransaction finishWct,
+ SurfaceControl.Transaction finishT) {
+ // Check if the recent transition is finished by returning to the current split, so we
+ // can restore the divider bar.
+ for (int i = 0; i < finishWct.getHierarchyOps().size(); ++i) {
+ final WindowContainerTransaction.HierarchyOp op =
+ finishWct.getHierarchyOps().get(i);
+ final IBinder container = op.getContainer();
+ if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
+ && (mMainStage.containsContainer(container)
+ || mSideStage.containsContainer(container))) {
+ setDividerVisibility(true, finishT);
+ return;
+ }
+ }
+
+ // Dismiss the split screen if it's not returning to split.
+ prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, finishWct);
+ setSplitsVisible(false);
+ setDividerVisibility(false, finishT);
+ logExit(EXIT_REASON_UNKNOWN);
+ }
+ };
+
StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
ShellTaskOrganizer taskOrganizer, DisplayController displayController,
DisplayImeController displayImeController,
@@ -337,15 +365,23 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
targetStage.evictAllChildren(evictWct);
targetStage.addTask(task, wct);
- if (!evictWct.isEmpty()) {
- wct.merge(evictWct, true /* transfer */);
- }
if (ENABLE_SHELL_TRANSITIONS) {
prepareEnterSplitScreen(wct);
- mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE,
- wct, null, this);
+ mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct,
+ null, this, new SplitScreenTransitions.TransitionCallback() {
+ @Override
+ public void onTransitionFinished(WindowContainerTransaction finishWct,
+ SurfaceControl.Transaction finishT) {
+ if (!evictWct.isEmpty()) {
+ finishWct.merge(evictWct, true);
+ }
+ }
+ });
} else {
+ if (!evictWct.isEmpty()) {
+ wct.merge(evictWct, true /* transfer */);
+ }
mTaskOrganizer.applyTransaction(wct);
}
return true;
@@ -365,6 +401,39 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return result;
}
+ /** Launches an activity into split. */
+ void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
+ @Nullable Bundle options) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+ prepareEvictChildTasks(position, evictWct);
+
+ options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */);
+ wct.sendPendingIntent(intent, fillInIntent, options);
+ prepareEnterSplitScreen(wct, null /* taskInfo */, position);
+
+ mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct, null, this,
+ new SplitScreenTransitions.TransitionCallback() {
+ @Override
+ public void onTransitionConsumed(boolean aborted) {
+ // Switch the split position if launching as MULTIPLE_TASK failed.
+ if (aborted
+ && (fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
+ setSideStagePositionAnimated(
+ SplitLayout.reversePosition(mSideStagePosition));
+ }
+ }
+
+ @Override
+ public void onTransitionFinished(WindowContainerTransaction finishWct,
+ SurfaceControl.Transaction finishT) {
+ if (!evictWct.isEmpty()) {
+ finishWct.merge(evictWct, true);
+ }
+ }
+ });
+ }
+
/** Starts 2 tasks in one transition. */
void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId,
@Nullable Bundle sideOptions, @SplitPosition int sidePosition, float splitRatio,
@@ -395,7 +464,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
wct.startTask(sideTaskId, sideOptions);
mSplitTransitions.startEnterTransition(
- TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this);
+ TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this, null);
}
/** Starts 2 tasks in one legacy transition. */
@@ -617,11 +686,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
int getTaskId(@SplitPosition int splitPosition) {
- if (mSideStagePosition == splitPosition) {
- return mSideStage.getTopVisibleChildTaskId();
- } else {
- return mMainStage.getTopVisibleChildTaskId();
+ if (splitPosition == SPLIT_POSITION_UNDEFINED) {
+ return INVALID_TASK_ID;
}
+
+ return mSideStagePosition == splitPosition
+ ? mSideStage.getTopVisibleChildTaskId()
+ : mMainStage.getTopVisibleChildTaskId();
}
void setSideStagePositionAnimated(@SplitPosition int sideStagePosition) {
@@ -861,6 +932,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitLayout.init();
setDividerVisibility(true, t);
updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
+ t.show(mRootTaskLeash);
setSplitsVisible(true);
mShouldUpdateRecents = true;
updateRecentTasksSplitPair();
@@ -1543,14 +1615,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
} else if (activityType == ACTIVITY_TYPE_HOME
|| activityType == ACTIVITY_TYPE_RECENTS) {
// Enter overview panel, so start recent transition.
- mSplitTransitions.setRecentTransition(transition,
- request.getRemoteTransition());
+ mSplitTransitions.setRecentTransition(transition, request.getRemoteTransition(),
+ mRecentTransitionCallback);
} else if (mSplitTransitions.mPendingRecent == null) {
// If split-task is not controlled by recents animation
// and occluded by the other fullscreen task, dismiss both.
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, out);
- mSplitTransitions.setDismissTransition(transition,
- STAGE_TYPE_UNDEFINED, EXIT_REASON_UNKNOWN);
+ mSplitTransitions.setDismissTransition(
+ transition, STAGE_TYPE_UNDEFINED, EXIT_REASON_UNKNOWN);
}
}
} else {
@@ -1558,7 +1630,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// One task is appearing into split, prepare to enter split screen.
out = new WindowContainerTransaction();
prepareEnterSplitScreen(out);
- mSplitTransitions.mPendingEnter = transition;
+ mSplitTransitions.setEnterTransition(
+ transition, request.getRemoteTransition(), null /* callback */);
}
}
return out;
@@ -1614,10 +1687,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- if (transition != mSplitTransitions.mPendingEnter
- && transition != mSplitTransitions.mPendingRecent
- && (mSplitTransitions.mPendingDismiss == null
- || mSplitTransitions.mPendingDismiss.mTransition != transition)) {
+ if (!mSplitTransitions.isPendingTransition(transition)) {
// Not entering or exiting, so just do some house-keeping and validation.
// If we're not in split-mode, just abort so something else can handle it.
@@ -1664,12 +1734,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
boolean shouldAnimate = true;
- if (mSplitTransitions.mPendingEnter == transition) {
+ if (mSplitTransitions.isPendingEnter(transition)) {
shouldAnimate = startPendingEnterAnimation(transition, info, startTransaction);
- } else if (mSplitTransitions.mPendingRecent == transition) {
+ } else if (mSplitTransitions.isPendingRecent(transition)) {
shouldAnimate = startPendingRecentAnimation(transition, info, startTransaction);
- } else if (mSplitTransitions.mPendingDismiss != null
- && mSplitTransitions.mPendingDismiss.mTransition == transition) {
+ } else if (mSplitTransitions.isPendingDismiss(transition)) {
shouldAnimate = startPendingDismissAnimation(
mSplitTransitions.mPendingDismiss, info, startTransaction, finishTransaction);
}
@@ -1837,28 +1906,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return true;
}
- void onRecentTransitionFinished(WindowContainerTransaction wct,
- SurfaceControl.Transaction finishT) {
- // Check if the recent transition is finished by returning to the current split so we can
- // restore the divider bar.
- for (int i = 0; i < wct.getHierarchyOps().size(); ++i) {
- final WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i);
- final IBinder container = op.getContainer();
- if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
- && (mMainStage.containsContainer(container)
- || mSideStage.containsContainer(container))) {
- setDividerVisibility(true, finishT);
- return;
- }
- }
-
- // Dismiss the split screen is it's not returning to split.
- prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
- setSplitsVisible(false);
- setDividerVisibility(false, finishT);
- logExit(EXIT_REASON_UNKNOWN);
- }
-
private void addDividerBarToTransition(@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, boolean show) {
final SurfaceControl leash = mSplitLayout.getDividerLeash();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index b351f8fcf838..857f578fd8ed 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -42,6 +42,7 @@ import android.testing.TestableLooper;
import android.util.Rational;
import android.util.Size;
import android.view.DisplayInfo;
+import android.view.SurfaceControl;
import android.window.WindowContainerToken;
import com.android.wm.shell.MockSurfaceControlHelper;
@@ -150,7 +151,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
final Rational aspectRatio = new Rational(2, 1);
mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
- createPipParams(aspectRatio)), null /* leash */);
+ createPipParams(aspectRatio)), mock(SurfaceControl.class));
assertEquals(aspectRatio.floatValue(), mPipBoundsState.getAspectRatio(), 0.01f);
}
@@ -158,7 +159,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
@Test
public void onTaskAppeared_updatesLastPipComponentName() {
mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1, createPipParams(null)),
- null /* leash */);
+ mock(SurfaceControl.class));
assertEquals(mComponent1, mPipBoundsState.getLastPipComponentName());
}
@@ -169,7 +170,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
mSpiedPipTaskOrganizer.onTaskAppeared(
createTaskInfo(mComponent1, createPipParams(null), minSize),
- null /* leash */);
+ mock(SurfaceControl.class));
assertEquals(minSize, mPipBoundsState.getOverrideMinSize());
}
@@ -179,7 +180,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
final Rational startAspectRatio = new Rational(2, 1);
final Rational newAspectRatio = new Rational(1, 2);
mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
- createPipParams(startAspectRatio)), null /* leash */);
+ createPipParams(startAspectRatio)), mock(SurfaceControl.class));
// It is in entering transition, should defer onTaskInfoChanged callback in this case.
mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent1,
@@ -197,7 +198,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
final Rational startAspectRatio = new Rational(2, 1);
final Rational newAspectRatio = new Rational(1, 2);
mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
- createPipParams(startAspectRatio)), null /* leash */);
+ createPipParams(startAspectRatio)), mock(SurfaceControl.class));
mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent1,
@@ -210,7 +211,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
@Test
public void onTaskInfoChanged_inPip_updatesLastPipComponentName() {
mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
- createPipParams(null)), null /* leash */);
+ createPipParams(null)), mock(SurfaceControl.class));
mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent2,
@@ -222,7 +223,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
@Test
public void onTaskInfoChanged_inPip_updatesOverrideMinSize() {
mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
- createPipParams(null)), null /* leash */);
+ createPipParams(null)), mock(SurfaceControl.class));
mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
final Size minSize = new Size(400, 320);
@@ -235,7 +236,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
@Test
public void onTaskVanished_clearsPipBounds() {
mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
- createPipParams(null)), null /* leash */);
+ createPipParams(null)), mock(SurfaceControl.class));
mPipBoundsState.setBounds(new Rect(100, 100, 200, 150));
mSpiedPipTaskOrganizer.onTaskVanished(createTaskInfo(mComponent1, createPipParams(null)));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 304ca66dd3bb..1d038f4ee377 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -182,7 +182,7 @@ public class SplitTransitionTests extends ShellTestCase {
IBinder transition = mSplitScreenTransitions.startEnterTransition(
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(),
- new RemoteTransition(testRemote), mStageCoordinator);
+ new RemoteTransition(testRemote), mStageCoordinator, null);
mMainStage.onTaskAppeared(mMainChild, createMockSurface());
mSideStage.onTaskAppeared(mSideChild, createMockSurface());
boolean accepted = mStageCoordinator.startAnimation(transition, info,
@@ -422,7 +422,7 @@ public class SplitTransitionTests extends ShellTestCase {
TransitionInfo enterInfo = createEnterPairInfo();
IBinder enterTransit = mSplitScreenTransitions.startEnterTransition(
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(),
- new RemoteTransition(new TestRemoteTransition()), mStageCoordinator);
+ new RemoteTransition(new TestRemoteTransition()), mStageCoordinator, null);
mMainStage.onTaskAppeared(mMainChild, createMockSurface());
mSideStage.onTaskAppeared(mSideChild, createMockSurface());
mStageCoordinator.startAnimation(enterTransit, enterInfo,
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index ce331607c531..d1f10a640dbf 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -118,6 +118,10 @@ public class SecureSettings {
Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD,
Settings.Secure.FACE_UNLOCK_APP_ENABLED,
Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
+ Settings.Secure.FINGERPRINT_SIDE_FPS_KG_POWER_WINDOW,
+ Settings.Secure.FINGERPRINT_SIDE_FPS_BP_POWER_WINDOW,
+ Settings.Secure.FINGERPRINT_SIDE_FPS_ENROLL_TAP_WINDOW,
+ Settings.Secure.FINGERPRINT_SIDE_FPS_AUTH_DOWNTIME,
Settings.Secure.ACTIVE_UNLOCK_ON_WAKE,
Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT,
Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index e82bf0427509..fbbdd32c5886 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -51,6 +51,7 @@ public class GlobalSettingsValidators {
|| (val == BatteryManager.BATTERY_PLUGGED_AC)
|| (val == BatteryManager.BATTERY_PLUGGED_USB)
|| (val == BatteryManager.BATTERY_PLUGGED_WIRELESS)
+ || (val == BatteryManager.BATTERY_PLUGGED_DOCK)
|| (val
== (BatteryManager.BATTERY_PLUGGED_AC
| BatteryManager.BATTERY_PLUGGED_USB))
@@ -58,12 +59,29 @@ public class GlobalSettingsValidators {
== (BatteryManager.BATTERY_PLUGGED_AC
| BatteryManager.BATTERY_PLUGGED_WIRELESS))
|| (val
+ == (BatteryManager.BATTERY_PLUGGED_AC
+ | BatteryManager.BATTERY_PLUGGED_DOCK))
+ || (val
== (BatteryManager.BATTERY_PLUGGED_USB
| BatteryManager.BATTERY_PLUGGED_WIRELESS))
|| (val
+ == (BatteryManager.BATTERY_PLUGGED_USB
+ | BatteryManager.BATTERY_PLUGGED_DOCK))
+ || (val
+ == (BatteryManager.BATTERY_PLUGGED_WIRELESS
+ | BatteryManager.BATTERY_PLUGGED_DOCK))
+ || (val
== (BatteryManager.BATTERY_PLUGGED_AC
| BatteryManager.BATTERY_PLUGGED_USB
- | BatteryManager.BATTERY_PLUGGED_WIRELESS));
+ | BatteryManager.BATTERY_PLUGGED_WIRELESS))
+ || (val
+ == (BatteryManager.BATTERY_PLUGGED_AC
+ | BatteryManager.BATTERY_PLUGGED_USB
+ | BatteryManager.BATTERY_PLUGGED_DOCK))
+ || (val
+ == (BatteryManager.BATTERY_PLUGGED_USB
+ | BatteryManager.BATTERY_PLUGGED_WIRELESS
+ | BatteryManager.BATTERY_PLUGGED_DOCK));
} catch (NumberFormatException e) {
return false;
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 5d773789b206..4aadf72930aa 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -172,6 +172,11 @@ public class SecureSettingsValidators {
Secure.AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.FACE_UNLOCK_KEYGUARD_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.FACE_UNLOCK_DISMISSES_KEYGUARD, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.FINGERPRINT_SIDE_FPS_KG_POWER_WINDOW, NON_NEGATIVE_INTEGER_VALIDATOR);
+ VALIDATORS.put(Secure.FINGERPRINT_SIDE_FPS_BP_POWER_WINDOW, NON_NEGATIVE_INTEGER_VALIDATOR);
+ VALIDATORS.put(Secure.FINGERPRINT_SIDE_FPS_ENROLL_TAP_WINDOW,
+ NON_NEGATIVE_INTEGER_VALIDATOR);
+ VALIDATORS.put(Secure.FINGERPRINT_SIDE_FPS_AUTH_DOWNTIME, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.SHOW_MEDIA_WHEN_BYPASSING, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.FACE_UNLOCK_APP_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, BOOLEAN_VALIDATOR);
diff --git a/packages/SystemUI/src/com/android/keyguard/FontInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
index 962c0023cf3b..3d341af3b397 100644
--- a/packages/SystemUI/src/com/android/keyguard/FontInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.keyguard
+package com.android.systemui.animation
import android.graphics.fonts.Font
import android.graphics.fonts.FontVariationAxis
@@ -61,7 +61,7 @@ class FontInterpolator {
var index: Int,
val sortedAxes: MutableList<FontVariationAxis>
) {
- constructor(font: Font, axes: List<FontVariationAxis>):
+ constructor(font: Font, axes: List<FontVariationAxis>) :
this(font.sourceIdentifier,
font.ttcIndex,
axes.toMutableList().apply { sortBy { it.tag } }
diff --git a/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
index ade89af81bd7..f79b328190dd 100644
--- a/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.keyguard
+package com.android.systemui.animation
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
@@ -61,8 +61,8 @@ class TextAnimator(
private val invalidateCallback: () -> Unit
) {
// Following two members are for mutable for testing purposes.
- internal var textInterpolator: TextInterpolator = TextInterpolator(layout)
- internal var animator: ValueAnimator = ValueAnimator.ofFloat(1f).apply {
+ public var textInterpolator: TextInterpolator = TextInterpolator(layout)
+ public var animator: ValueAnimator = ValueAnimator.ofFloat(1f).apply {
duration = DEFAULT_ANIMATION_DURATION
addUpdateListener {
textInterpolator.progress = it.animatedValue as Float
@@ -279,4 +279,4 @@ private fun <V> SparseArray<V>.getOrElse(key: Int, defaultValue: () -> V): V {
put(key, v)
}
return v
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
index 20dbe29efb35..ff64c7891128 100644
--- a/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.keyguard
+package com.android.systemui.animation
import android.graphics.Canvas
import android.graphics.Paint
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 87a9825af1cb..6a38507b2ad7 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -31,7 +31,7 @@
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:paddingStart="@dimen/clock_padding_start">
- <com.android.keyguard.AnimatableClockView
+ <com.android.systemui.shared.clocks.AnimatableClockView
android:id="@+id/animatable_clock_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -53,7 +53,7 @@
android:layout_height="wrap_content"
android:layout_below="@id/keyguard_slice_view"
android:visibility="gone">
- <com.android.keyguard.AnimatableClockView
+ <com.android.systemui.shared.clocks.AnimatableClockView
android:id="@+id/animatable_clock_view_large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res-keyguard/values/attrs.xml b/packages/SystemUI/res-keyguard/values/attrs.xml
index 25be37adac5b..f2a1e26dc9ce 100644
--- a/packages/SystemUI/res-keyguard/values/attrs.xml
+++ b/packages/SystemUI/res-keyguard/values/attrs.xml
@@ -41,10 +41,4 @@
<attr name="passwordStyle" format="reference" />
<attr name="numPadKeyStyle" format="reference" />
-
- <declare-styleable name="AnimatableClockView">
- <attr name="dozeWeight" format="integer" />
- <attr name="lockScreenWeight" format="integer" />
- <attr name="chargeAnimationDelay" format="integer" />
- </declare-styleable>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values/donottranslate.xml b/packages/SystemUI/res-keyguard/values/donottranslate.xml
index e677797dc4a7..f9872f8da192 100644
--- a/packages/SystemUI/res-keyguard/values/donottranslate.xml
+++ b/packages/SystemUI/res-keyguard/values/donottranslate.xml
@@ -23,11 +23,5 @@
<!-- Skeleton string format for displaying the date shorter. -->
<string name="abbrev_month_day_no_year">MMMd</string>
- <!-- Skeleton string format for displaying the time in 12-hour format. -->
- <string name="clock_12hr_format">hm</string>
-
- <!-- Skeleton string format for displaying the time in 24-hour format. -->
- <string name="clock_24hr_format">Hm</string>
-
<string name="num_pad_key_ratio">1</string>
</resources>
diff --git a/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml b/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml
index e929169cfe3d..a97c90c5e8ac 100644
--- a/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml
+++ b/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml
@@ -54,7 +54,6 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/rounded_bg_full_large_radius"
- android:onClick="dismissActivity"
android:text="@string/got_it"
android:textColor="?androidprv:attr/textColorOnAccent"
android:layout_marginBottom="60dp"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0cafa355d847..0a4c24d0eaa6 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -59,8 +59,8 @@
<dimen name="navigation_edge_entry_margin">4dp</dimen>
<dimen name="navigation_edge_entry_background_width">8dp</dimen>
<dimen name="navigation_edge_entry_background_height">60dp</dimen>
- <dimen name="navigation_edge_entry_edge_corners">6dp</dimen>
- <dimen name="navigation_edge_entry_far_corners">6dp</dimen>
+ <dimen name="navigation_edge_entry_edge_corners">30dp</dimen>
+ <dimen name="navigation_edge_entry_far_corners">30dp</dimen>
<dimen name="navigation_edge_entry_arrow_length">10dp</dimen>
<dimen name="navigation_edge_entry_arrow_height">7dp</dimen>
@@ -90,6 +90,7 @@
<dimen name="navigation_edge_cancelled_arrow_length">12dp</dimen>
<dimen name="navigation_edge_cancelled_arrow_height">0dp</dimen>
+ <dimen name="navigation_edge_cancelled_edge_corners">6dp</dimen>
<!-- Height of notification icons in the status bar -->
<dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen>
@@ -898,7 +899,7 @@
<!-- The maximum offset for the under-display fingerprint sensor (UDFPS) icon in either
direction that elements are moved to prevent burn-in on AOD-->
<dimen name="udfps_burn_in_offset_x">7px</dimen>
- <dimen name="udfps_burn_in_offset_y">28px</dimen>
+ <dimen name="udfps_burn_in_offset_y">20px</dimen>
<!-- The absolute side margins of quick settings -->
<dimen name="quick_settings_bottom_margin_media">8dp</dimen>
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 114ea657a758..165f9eba6f4c 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -47,6 +47,7 @@ android_library {
],
static_libs: [
"PluginCoreLib",
+ "SystemUIAnimationLib",
"SystemUIUnfoldLib",
"androidx.dynamicanimation_dynamicanimation",
"androidx.concurrent_concurrent-futures",
diff --git a/packages/SystemUI/shared/res/values/attrs.xml b/packages/SystemUI/shared/res/values/attrs.xml
new file mode 100644
index 000000000000..f9d66ee583da
--- /dev/null
+++ b/packages/SystemUI/shared/res/values/attrs.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Formatting note: terminate all comments with a period, to avoid breaking
+ the documentation output. To suppress comment lines from the documentation
+ output, insert an eat-comment element after the comment lines.
+-->
+
+<resources>
+ <declare-styleable name="AnimatableClockView">
+ <attr name="dozeWeight" format="integer" />
+ <attr name="lockScreenWeight" format="integer" />
+ <attr name="chargeAnimationDelay" format="integer" />
+ </declare-styleable>
+</resources>
diff --git a/packages/SystemUI/shared/res/values/donottranslate.xml b/packages/SystemUI/shared/res/values/donottranslate.xml
new file mode 100644
index 000000000000..383d5521f156
--- /dev/null
+++ b/packages/SystemUI/shared/res/values/donottranslate.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Skeleton string format for displaying the time in 12-hour format. -->
+ <string name="clock_12hr_format">hm</string>
+
+ <!-- Skeleton string format for displaying the time in 24-hour format. -->
+ <string name="clock_24hr_format">Hm</string>
+</resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index b4955d28f84b..5b1a23d4f364 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.keyguard
+package com.android.systemui.shared.clocks
import android.animation.TimeInterpolator
import android.annotation.ColorInt
@@ -26,9 +26,10 @@ import android.text.TextUtils
import android.text.format.DateFormat
import android.util.AttributeSet
import android.widget.TextView
-import com.android.systemui.R
+import com.android.systemui.animation.GlyphCallback
import com.android.systemui.animation.Interpolators
-import com.android.systemui.statusbar.notification.stack.StackStateAnimator
+import com.android.systemui.animation.TextAnimator
+import com.android.systemui.shared.R
import java.io.PrintWriter
import java.util.Calendar
import java.util.Locale
@@ -38,13 +39,13 @@ import java.util.TimeZone
* Displays the time with the hour positioned above the minutes. (ie: 09 above 30 is 9:30)
* The time's text color is a gradient that changes its colors based on its controller.
*/
+@SuppressLint("AppCompatCustomView")
class AnimatableClockView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : TextView(context, attrs, defStyleAttr, defStyleRes) {
- private val tag = "AnimatableClockView"
private var lastMeasureCall: CharSequence = ""
@@ -193,7 +194,7 @@ class AnimatableClockView @JvmOverloads constructor(
)
}
- fun animateFoldAppear() {
+ fun animateFoldAppear(animate: Boolean = true) {
if (textAnimator == null) {
return
}
@@ -210,22 +211,22 @@ class AnimatableClockView @JvmOverloads constructor(
weight = dozingWeightInternal,
textSize = -1f,
color = dozingColor,
- animate = true,
+ animate = animate,
interpolator = Interpolators.EMPHASIZED_DECELERATE,
- duration = StackStateAnimator.ANIMATION_DURATION_FOLD_TO_AOD.toLong(),
+ duration = ANIMATION_DURATION_FOLD_TO_AOD.toLong(),
delay = 0,
onAnimationEnd = null
)
}
- fun animateCharge(dozeStateGetter: DozeStateGetter) {
+ fun animateCharge(isDozing: () -> Boolean) {
if (textAnimator == null || textAnimator!!.isRunning()) {
// Skip charge animation if dozing animation is already playing.
return
}
val startAnimPhase2 = Runnable {
setTextStyle(
- weight = if (dozeStateGetter.isDozing) dozingWeight else lockScreenWeight,
+ weight = if (isDozing()) dozingWeight else lockScreenWeight,
textSize = -1f,
color = null,
animate = true,
@@ -235,7 +236,7 @@ class AnimatableClockView @JvmOverloads constructor(
)
}
setTextStyle(
- weight = if (dozeStateGetter.isDozing) lockScreenWeight else dozingWeight,
+ weight = if (isDozing()) lockScreenWeight else dozingWeight,
textSize = -1f,
color = null,
animate = true,
@@ -385,14 +386,14 @@ class AnimatableClockView @JvmOverloads constructor(
}
}
- interface DozeStateGetter {
- val isDozing: Boolean
+ companion object {
+ private val TAG = AnimatableClockView::class.simpleName
+ const val ANIMATION_DURATION_FOLD_TO_AOD: Int = 600
+ private const val DOUBLE_LINE_FORMAT_12_HOUR = "hh\nmm"
+ private const val DOUBLE_LINE_FORMAT_24_HOUR = "HH\nmm"
+ private const val DOZE_ANIM_DURATION: Long = 300
+ private const val APPEAR_ANIM_DURATION: Long = 350
+ private const val CHARGE_ANIM_DURATION_PHASE_0: Long = 500
+ private const val CHARGE_ANIM_DURATION_PHASE_1: Long = 1000
}
}
-
-private const val DOUBLE_LINE_FORMAT_12_HOUR = "hh\nmm"
-private const val DOUBLE_LINE_FORMAT_24_HOUR = "HH\nmm"
-private const val DOZE_ANIM_DURATION: Long = 300
-private const val APPEAR_ANIM_DURATION: Long = 350
-private const val CHARGE_ANIM_DURATION_PHASE_0: Long = 500
-private const val CHARGE_ANIM_DURATION_PHASE_1: Long = 1000
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index 487e1a48044c..c69ff7ee1cd8 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -32,6 +32,7 @@ import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.clocks.AnimatableClockView;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.ViewController;
@@ -134,6 +135,21 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie
reset();
}
}
+
+ @Override
+ public void onTimeFormatChanged(String timeFormat) {
+ mView.refreshFormat();
+ }
+
+ @Override
+ public void onTimeZoneChanged(TimeZone timeZone) {
+ mView.onTimeZoneChanged(timeZone);
+ }
+
+ @Override
+ public void onUserSwitchComplete(int userId) {
+ mView.refreshFormat();
+ }
};
@Override
@@ -186,7 +202,7 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie
/** Animate the clock appearance when a foldable device goes from fully-open/half-open state to
* fully folded state and it goes to sleep (always on display screen) */
public void animateFoldAppear() {
- mView.animateFoldAppear();
+ mView.animateFoldAppear(true);
}
/**
@@ -197,20 +213,6 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie
}
/**
- * Updates the timezone for the view.
- */
- public void onTimeZoneChanged(TimeZone timeZone) {
- mView.onTimeZoneChanged(timeZone);
- }
-
- /**
- * Trigger a time format update
- */
- public void refreshFormat() {
- mView.refreshFormat();
- }
-
- /**
* Return locallly stored dozing state.
*/
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 40edfe50d907..206b8bee323c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -22,6 +22,7 @@ import com.android.keyguard.dagger.KeyguardStatusViewScope;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.ClockPlugin;
+import com.android.systemui.shared.clocks.AnimatableClockView;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 5c9dd5ec26e7..6c32a4910c56 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -398,17 +398,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
void updateTimeZone(TimeZone timeZone) {
mView.onTimeZoneChanged(timeZone);
- if (mClockViewController != null) {
- mClockViewController.onTimeZoneChanged(timeZone);
- mLargeClockViewController.onTimeZoneChanged(timeZone);
- }
- }
-
- void refreshFormat() {
- if (mClockViewController != null) {
- mClockViewController.refreshFormat();
- mLargeClockViewController.refreshFormat();
- }
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 8921780fdacf..014d08288158 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -240,11 +240,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
}
@Override
- public void onTimeFormatChanged(String timeFormat) {
- mKeyguardClockSwitchController.refreshFormat();
- }
-
- @Override
public void onTimeZoneChanged(TimeZone timeZone) {
mKeyguardClockSwitchController.updateTimeZone(timeZone);
}
@@ -256,11 +251,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
refreshTime();
}
}
-
- @Override
- public void onUserSwitchComplete(int userId) {
- mKeyguardClockSwitchController.refreshFormat();
- }
};
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index c19175742ce5..4ae2cad4a01f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -805,8 +805,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private Runnable mRetryFingerprintAuthentication = new Runnable() {
@Override
public void run() {
- Log.w(TAG, "Retrying fingerprint after HW unavailable, attempt " +
- mHardwareFingerprintUnavailableRetryCount);
+ Log.w(TAG,
+ "Retrying fingerprint attempt: " + mHardwareFingerprintUnavailableRetryCount);
if (mFpm.isHardwareDetected()) {
updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
} else if (mHardwareFingerprintUnavailableRetryCount < HAL_ERROR_RETRY_MAX) {
@@ -833,7 +833,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
}
- if (msgId == FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE) {
+ if (msgId == FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE
+ || msgId == FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED) {
+ Log.d(TAG, "Fingerprint retrying auth due to(" + msgId + ") -> " + errString);
mHandler.postDelayed(mRetryFingerprintAuthentication, HAL_ERROR_RETRY_TIMEOUT);
}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index f99293a9d148..edcaf492874d 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -842,7 +842,8 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
| WindowManager.LayoutParams.FLAG_SLIPPERY
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
PixelFormat.TRANSLUCENT);
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
| WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 029cabb0bc0b..718befadd2b5 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -31,6 +31,7 @@ import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver;
import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender;
import com.android.systemui.people.PeopleProvider;
+import com.android.systemui.statusbar.pipeline.ConnectivityInfoProcessor;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.unfold.FoldStateLogger;
import com.android.systemui.unfold.FoldStateLoggingProvider;
@@ -130,6 +131,7 @@ public interface SysUIComponent {
getMediaTttCommandLineHelper();
getMediaMuteAwaitConnectionCli();
getNearbyMediaDevicesManager();
+ getConnectivityInfoProcessor();
getUnfoldLatencyTracker().init();
getFoldStateLoggingProvider().ifPresent(FoldStateLoggingProvider::init);
getFoldStateLogger().ifPresent(FoldStateLogger::init);
@@ -212,6 +214,9 @@ public interface SysUIComponent {
/** */
Optional<NearbyMediaDevicesManager> getNearbyMediaDevicesManager();
+ /** */
+ Optional<ConnectivityInfoProcessor> getConnectivityInfoProcessor();
+
/**
* Returns {@link CoreStartable}s that should be started with the application.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index fe9622250e67..ceb702eafe1e 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -70,7 +70,7 @@ import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfC
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
-import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -91,7 +91,6 @@ import com.android.systemui.wallet.dagger.WalletModule;
import com.android.systemui.wmshell.BubblesManager;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.dagger.DynamicOverride;
-import com.android.wm.shell.sysui.ShellController;
import java.util.Optional;
import java.util.concurrent.Executor;
@@ -135,6 +134,7 @@ import dagger.Provides;
SettingsUtilModule.class,
SmartRepliesInflationModule.class,
SmartspaceModule.class,
+ StatusBarPipelineModule.class,
StatusBarPolicyModule.class,
StatusBarWindowModule.class,
SysUIConcurrencyModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 65711edd6e24..845dab24be9c 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -148,6 +148,8 @@ public class Flags {
public static final BooleanFlag STATUS_BAR_LETTERBOX_APPEARANCE =
new BooleanFlag(603, false);
+ public static final BooleanFlag NEW_STATUS_BAR_PIPELINE = new BooleanFlag(604, false);
+
/***************************************/
// 700 - dialer/calls
public static final BooleanFlag ONGOING_CALL_STATUS_BAR_CHIP =
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index 2006d5338c8c..b05e75ea1c97 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -601,7 +601,7 @@ class BackPanelController private constructor(
if (currentState == GestureState.INACTIVE ||
currentState == GestureState.CANCELLED
)
- params.entryIndicator.backgroundDimens.edgeCornerRadius
+ params.cancelledEdgeCornerRadius
else
params.activeIndicator.backgroundDimens.edgeCornerRadius
)
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
index a3fb58d5b015..d56537b694da 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
@@ -33,6 +33,8 @@ data class EdgePanelParams(private var resources: Resources) {
private set
var fullyStretchedIndicator = BackIndicatorDimens()
private set
+ var cancelledEdgeCornerRadius: Float = 0f
+ private set
var cancelledArrowDimens = ArrowDimens()
// navigation bar edge constants
@@ -132,6 +134,8 @@ data class EdgePanelParams(private var resources: Resources) {
)
)
+ cancelledEdgeCornerRadius = getDimen(R.dimen.navigation_edge_cancelled_edge_corners)
+
cancelledArrowDimens = ArrowDimens(
length = getDimen(R.dimen.navigation_edge_cancelled_arrow_length),
height = getDimen(R.dimen.navigation_edge_cancelled_arrow_height)
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index e845aa85a121..7cc95a158a14 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -22,7 +22,6 @@ import static android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
-import android.view.View;
import android.view.ViewGroup;
import androidx.activity.ComponentActivity;
@@ -40,7 +39,6 @@ public class PeopleSpaceActivity extends ComponentActivity {
private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
private final PeopleViewModel.Factory mViewModelFactory;
- private PeopleViewModel mViewModel;
@Inject
public PeopleSpaceActivity(PeopleViewModel.Factory viewModelFactory) {
@@ -52,38 +50,32 @@ public class PeopleSpaceActivity extends ComponentActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setResult(RESULT_CANCELED);
- mViewModel = new ViewModelProvider(this, mViewModelFactory).get(PeopleViewModel.class);
+
+ PeopleViewModel viewModel = new ViewModelProvider(this, mViewModelFactory).get(
+ PeopleViewModel.class);
// Update the widget ID coming from the intent.
int widgetId = getIntent().getIntExtra(EXTRA_APPWIDGET_ID, INVALID_APPWIDGET_ID);
- mViewModel.onWidgetIdChanged(widgetId);
+ viewModel.onWidgetIdChanged(widgetId);
ViewGroup view = PeopleViewBinder.create(this);
- PeopleViewBinder.bind(view, mViewModel, /* lifecycleOwner= */ this,
- () -> {
- finishActivity();
+ PeopleViewBinder.bind(view, viewModel, /* lifecycleOwner= */ this,
+ (result) -> {
+ finishActivity(result);
return null;
});
setContentView(view);
}
- /** Finish activity with a successful widget configuration result. */
- private void finishActivity() {
- if (DEBUG) Log.d(TAG, "Widget added!");
- setActivityResult(RESULT_OK);
+ private void finishActivity(PeopleViewModel.Result result) {
+ if (result instanceof PeopleViewModel.Result.Success) {
+ if (DEBUG) Log.d(TAG, "Widget added!");
+ Intent data = ((PeopleViewModel.Result.Success) result).getData();
+ setResult(RESULT_OK, data);
+ } else {
+ if (DEBUG) Log.d(TAG, "Activity dismissed with no widgets added!");
+ setResult(RESULT_CANCELED);
+ }
finish();
}
-
- /** Finish activity without choosing a widget. */
- public void dismissActivity(View v) {
- if (DEBUG) Log.d(TAG, "Activity dismissed with no widgets added!");
- setResult(RESULT_CANCELED);
- finish();
- }
-
- private void setActivityResult(int result) {
- Intent resultValue = new Intent();
- resultValue.putExtra(EXTRA_APPWIDGET_ID, mViewModel.getAppWidgetId().getValue());
- setResult(result, resultValue);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt b/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt
index bc982cccaacd..d8a429e5bb1a 100644
--- a/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt
@@ -41,7 +41,7 @@ import kotlinx.coroutines.launch
/** A ViewBinder for [PeopleViewModel]. */
object PeopleViewBinder {
- private const val TAG = "PeopleSpaceViewBinder"
+ private const val TAG = "PeopleViewBinder"
/**
* The [ViewOutlineProvider] used to clip the corner radius of the recent and priority lists.
@@ -72,15 +72,15 @@ object PeopleViewBinder {
view: ViewGroup,
viewModel: PeopleViewModel,
lifecycleOwner: LifecycleOwner,
- onFinish: () -> Unit,
+ onResult: (PeopleViewModel.Result) -> Unit,
) {
- // Call [onFinish] this activity when the ViewModel tells us so.
+ // Call [onResult] as soon as a result is available.
lifecycleOwner.lifecycleScope.launch {
lifecycleOwner.repeatOnLifecycle(CREATED) {
- viewModel.isFinished.collect { isFinished ->
- if (isFinished) {
- viewModel.clearIsFinished()
- onFinish()
+ viewModel.result.collect { result ->
+ if (result != null) {
+ viewModel.clearResult()
+ onResult(result)
}
}
}
@@ -104,7 +104,7 @@ object PeopleViewBinder {
viewModel::onTileClicked,
)
} else {
- setNoConversationsContent(view)
+ setNoConversationsContent(view, viewModel::onUserJourneyCancelled)
}
}
}
@@ -119,7 +119,7 @@ object PeopleViewBinder {
}
}
- private fun setNoConversationsContent(view: ViewGroup) {
+ private fun setNoConversationsContent(view: ViewGroup, onGotItClicked: () -> Unit) {
// This should never happen.
if (view.childCount > 1) {
error("view has ${view.childCount} children, it should have maximum 1")
@@ -140,6 +140,10 @@ object PeopleViewBinder {
LayoutInflater.from(context)
.inflate(R.layout.people_space_activity_no_conversations, /* root= */ view)
+ noConversationsView.findViewById<View>(R.id.got_it_button).setOnClickListener {
+ onGotItClicked()
+ }
+
// The Tile preview has colorBackground as its background. Change it so it's different than
// the activity's background.
val item = noConversationsView.findViewById<LinearLayout>(android.R.id.background)
diff --git a/packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleViewModel.kt b/packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleViewModel.kt
index 17de991588b8..0834a5ae75f6 100644
--- a/packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleViewModel.kt
@@ -16,8 +16,10 @@
package com.android.systemui.people.ui.viewmodel
+import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID
import android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID
import android.content.Context
+import android.content.Intent
import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
@@ -32,6 +34,7 @@ import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
/**
* Models UI state for the people space, allowing the user to select which conversation should be
@@ -49,7 +52,7 @@ class PeopleViewModel(
* reactive and you have to manually call [onTileRefreshRequested] to refresh the tiles.
*/
private val _priorityTiles = MutableStateFlow(priorityTiles())
- val priorityTiles: Flow<List<PeopleTileViewModel>> = _priorityTiles
+ val priorityTiles: Flow<List<PeopleTileViewModel>> = _priorityTiles.asStateFlow()
/**
* The list of the priority tiles/conversations.
@@ -58,15 +61,15 @@ class PeopleViewModel(
* reactive and you have to manually call [onTileRefreshRequested] to refresh the tiles.
*/
private val _recentTiles = MutableStateFlow(recentTiles())
- val recentTiles: Flow<List<PeopleTileViewModel>> = _recentTiles
+ val recentTiles: Flow<List<PeopleTileViewModel>> = _recentTiles.asStateFlow()
/** The ID of the widget currently being edited/added. */
private val _appWidgetId = MutableStateFlow(INVALID_APPWIDGET_ID)
- val appWidgetId: StateFlow<Int> = _appWidgetId
+ val appWidgetId: StateFlow<Int> = _appWidgetId.asStateFlow()
- /** Whether the user journey is complete. */
- private val _isFinished = MutableStateFlow(false)
- val isFinished: StateFlow<Boolean> = _isFinished
+ /** The result of this user journey. */
+ private val _result = MutableStateFlow<Result?>(null)
+ val result: StateFlow<Result?> = _result.asStateFlow()
/** Refresh the [priorityTiles] and [recentTiles]. */
fun onTileRefreshRequested() {
@@ -79,22 +82,28 @@ class PeopleViewModel(
_appWidgetId.value = widgetId
}
- /** Clear [isFinished], setting it to false. */
- fun clearIsFinished() {
- _isFinished.value = false
+ /** Clear [result], setting it to null. */
+ fun clearResult() {
+ _result.value = null
}
/** Called when a tile is clicked. */
fun onTileClicked(tile: PeopleTileViewModel) {
+ val widgetId = _appWidgetId.value
if (PeopleSpaceUtils.DEBUG) {
Log.d(
TAG,
- "Put ${tile.username}'s shortcut ID: ${tile.key.shortcutId} for widget ID: " +
- _appWidgetId.value
+ "Put ${tile.username}'s shortcut ID: ${tile.key.shortcutId} for widget ID $widgetId"
)
}
- widgetRepository.setWidgetTile(_appWidgetId.value, tile.key)
- _isFinished.value = true
+ widgetRepository.setWidgetTile(widgetId, tile.key)
+ _result.value =
+ Result.Success(Intent().apply { putExtra(EXTRA_APPWIDGET_ID, appWidgetId.value) })
+ }
+
+ /** Called when this user journey is cancelled. */
+ fun onUserJourneyCancelled() {
+ _result.value = Result.Cancelled
}
private fun priorityTiles(): List<PeopleTileViewModel> {
@@ -143,7 +152,12 @@ class PeopleViewModel(
}
}
+ sealed class Result {
+ class Success(val data: Intent) : Result()
+ object Cancelled : Result()
+ }
+
companion object {
- private const val TAG = "PeopleSpaceViewModel"
+ private const val TAG = "PeopleViewModel"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 7c9df426aba6..174bf4c27de8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -25,6 +25,7 @@ import android.view.View;
import com.android.keyguard.KeyguardSliceView;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
+import com.android.systemui.shared.clocks.AnimatableClockView;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -52,7 +53,8 @@ public class StackStateAnimator {
public static final int ANIMATION_DURATION_CLOSE_REMOTE_INPUT = 150;
public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 400;
public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 400;
- public static final int ANIMATION_DURATION_FOLD_TO_AOD = 600;
+ public static final int ANIMATION_DURATION_FOLD_TO_AOD =
+ AnimatableClockView.ANIMATION_DURATION_FOLD_TO_AOD;
public static final int ANIMATION_DURATION_PULSE_APPEAR =
KeyguardSliceView.DEFAULT_ANIM_DURATION;
public static final int ANIMATION_DURATION_BLOCKING_HELPER_FADE = 240;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index eda3446c8cb6..f4b7772a1453 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -57,10 +57,8 @@ import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.qs.QSPanelController;
import com.android.systemui.shade.NotificationPanelView;
import com.android.systemui.shade.NotificationPanelViewController;
-import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DisableFlagsLogger;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
@@ -91,19 +89,16 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final AssistManager mAssistManager;
private final DozeServiceHost mDozeServiceHost;
- private final SysuiStatusBarStateController mStatusBarStateController;
- private final NotificationShadeWindowView mNotificationShadeWindowView;
private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
private final PowerManager mPowerManager;
private final VibratorHelper mVibratorHelper;
private final Optional<Vibrator> mVibratorOptional;
- private final LightBarController mLightBarController;
private final DisableFlagsLogger mDisableFlagsLogger;
private final int mDisplayId;
private final boolean mVibrateOnOpening;
private final VibrationEffect mCameraLaunchGestureVibrationEffect;
-
+ private final SystemBarAttributesListener mSystemBarAttributesListener;
private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
@@ -126,16 +121,14 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
AssistManager assistManager,
DozeServiceHost dozeServiceHost,
- SysuiStatusBarStateController statusBarStateController,
- NotificationShadeWindowView notificationShadeWindowView,
NotificationStackScrollLayoutController notificationStackScrollLayoutController,
StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
PowerManager powerManager,
VibratorHelper vibratorHelper,
Optional<Vibrator> vibratorOptional,
- LightBarController lightBarController,
DisableFlagsLogger disableFlagsLogger,
- @DisplayId int displayId) {
+ @DisplayId int displayId,
+ SystemBarAttributesListener systemBarAttributesListener) {
mCentralSurfaces = centralSurfaces;
mContext = context;
@@ -152,20 +145,18 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mAssistManager = assistManager;
mDozeServiceHost = dozeServiceHost;
- mStatusBarStateController = statusBarStateController;
- mNotificationShadeWindowView = notificationShadeWindowView;
mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager;
mPowerManager = powerManager;
mVibratorHelper = vibratorHelper;
mVibratorOptional = vibratorOptional;
- mLightBarController = lightBarController;
mDisableFlagsLogger = disableFlagsLogger;
mDisplayId = displayId;
mVibrateOnOpening = resources.getBoolean(R.bool.config_vibrateOnIconAnimation);
mCameraLaunchGestureVibrationEffect = getCameraGestureVibrationEffect(
mVibratorOptional, resources);
+ mSystemBarAttributesListener = systemBarAttributesListener;
}
@Override
@@ -472,14 +463,18 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
if (displayId != mDisplayId) {
return;
}
- boolean barModeChanged = mCentralSurfaces.setAppearance(appearance);
-
- mLightBarController.onStatusBarAppearanceChanged(appearanceRegions, barModeChanged,
- mCentralSurfaces.getBarMode(), navbarColorManagedByIme);
-
- mCentralSurfaces.updateBubblesVisibility();
- mStatusBarStateController.setSystemBarAttributes(
- appearance, behavior, requestedVisibilities, packageName);
+ // SystemBarAttributesListener should __always__ be the top-level listener for system bar
+ // attributes changed.
+ mSystemBarAttributesListener.onSystemBarAttributesChanged(
+ displayId,
+ appearance,
+ appearanceRegions,
+ navbarColorManagedByIme,
+ behavior,
+ requestedVisibilities,
+ packageName,
+ letterboxDetails
+ );
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt
new file mode 100644
index 000000000000..a0415f2f3d7c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import android.view.InsetsVisibilities
+import android.view.WindowInsetsController.Appearance
+import android.view.WindowInsetsController.Behavior
+import com.android.internal.statusbar.LetterboxDetails
+import com.android.internal.view.AppearanceRegion
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/**
+ * Top-level listener of system attributes changed. This class is __always the first__ one to be
+ * notified about changes.
+ *
+ * It is responsible for modifying any attributes if necessary, and then notifying the other
+ * downstream listeners.
+ */
+@CentralSurfacesScope
+class SystemBarAttributesListener
+@Inject
+internal constructor(
+ private val centralSurfaces: CentralSurfaces,
+ private val featureFlags: FeatureFlags,
+ private val letterboxAppearanceCalculator: LetterboxAppearanceCalculator,
+ private val statusBarStateController: SysuiStatusBarStateController,
+ private val lightBarController: LightBarController,
+ private val dumpManager: DumpManager,
+) : CentralSurfacesComponent.Startable, StatusBarBoundsProvider.BoundsChangeListener {
+
+ private var lastLetterboxAppearance: LetterboxAppearance? = null
+ private var lastSystemBarAttributesParams: SystemBarAttributesParams? = null
+
+ override fun start() {
+ dumpManager.registerDumpable(javaClass.simpleName, this::dump)
+ }
+
+ override fun stop() {
+ dumpManager.unregisterDumpable(javaClass.simpleName)
+ }
+
+ override fun onStatusBarBoundsChanged() {
+ val params = lastSystemBarAttributesParams
+ if (params != null && shouldUseLetterboxAppearance(params.letterboxesArray)) {
+ onSystemBarAttributesChanged(
+ params.displayId,
+ params.appearance,
+ params.appearanceRegionsArray,
+ params.navbarColorManagedByIme,
+ params.behavior,
+ params.requestedVisibilities,
+ params.packageName,
+ params.letterboxesArray)
+ }
+ }
+
+ fun onSystemBarAttributesChanged(
+ displayId: Int,
+ @Appearance originalAppearance: Int,
+ originalAppearanceRegions: Array<AppearanceRegion>,
+ navbarColorManagedByIme: Boolean,
+ @Behavior behavior: Int,
+ requestedVisibilities: InsetsVisibilities,
+ packageName: String,
+ letterboxDetails: Array<LetterboxDetails>
+ ) {
+ lastSystemBarAttributesParams =
+ SystemBarAttributesParams(
+ displayId,
+ originalAppearance,
+ originalAppearanceRegions.toList(),
+ navbarColorManagedByIme,
+ behavior,
+ requestedVisibilities,
+ packageName,
+ letterboxDetails.toList())
+
+ val (appearance, appearanceRegions) =
+ modifyAppearanceIfNeeded(
+ originalAppearance, originalAppearanceRegions, letterboxDetails)
+
+ val barModeChanged = centralSurfaces.setAppearance(appearance)
+
+ lightBarController.onStatusBarAppearanceChanged(
+ appearanceRegions, barModeChanged, centralSurfaces.barMode, navbarColorManagedByIme)
+
+ centralSurfaces.updateBubblesVisibility()
+ statusBarStateController.setSystemBarAttributes(
+ appearance, behavior, requestedVisibilities, packageName)
+ }
+
+ private fun modifyAppearanceIfNeeded(
+ appearance: Int,
+ appearanceRegions: Array<AppearanceRegion>,
+ letterboxDetails: Array<LetterboxDetails>
+ ): Pair<Int, Array<AppearanceRegion>> =
+ if (shouldUseLetterboxAppearance(letterboxDetails)) {
+ val letterboxAppearance =
+ letterboxAppearanceCalculator.getLetterboxAppearance(
+ appearance, appearanceRegions, letterboxDetails)
+ lastLetterboxAppearance = letterboxAppearance
+ Pair(letterboxAppearance.appearance, letterboxAppearance.appearanceRegions)
+ } else {
+ lastLetterboxAppearance = null
+ Pair(appearance, appearanceRegions)
+ }
+
+ private fun shouldUseLetterboxAppearance(letterboxDetails: Array<LetterboxDetails>) =
+ isLetterboxAppearanceFlagEnabled() && letterboxDetails.isNotEmpty()
+
+ private fun isLetterboxAppearanceFlagEnabled() =
+ featureFlags.isEnabled(Flags.STATUS_BAR_LETTERBOX_APPEARANCE)
+
+ private fun dump(printWriter: PrintWriter, strings: Array<String>) {
+ printWriter.println("lastSystemBarAttributesParams: $lastSystemBarAttributesParams")
+ printWriter.println("lastLetterboxAppearance: $lastLetterboxAppearance")
+ printWriter.println("letterbox appearance flag: ${isLetterboxAppearanceFlagEnabled()}")
+ }
+}
+
+/**
+ * Keeps track of the parameters passed in
+ * [SystemBarAttributesListener.onSystemBarAttributesChanged].
+ */
+private data class SystemBarAttributesParams(
+ val displayId: Int,
+ @Appearance val appearance: Int,
+ val appearanceRegions: List<AppearanceRegion>,
+ val navbarColorManagedByIme: Boolean,
+ @Behavior val behavior: Int,
+ val requestedVisibilities: InsetsVisibilities,
+ val packageName: String,
+ val letterboxes: List<LetterboxDetails>,
+) {
+ val letterboxesArray = letterboxes.toTypedArray()
+ val appearanceRegionsArray = appearanceRegions.toTypedArray()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesStartableModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesStartableModule.java
index 590522fc8751..d57e6a791489 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesStartableModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesStartableModule.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone.dagger;
import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator;
+import com.android.systemui.statusbar.phone.SystemBarAttributesListener;
import java.util.Set;
@@ -34,4 +35,9 @@ interface CentralSurfacesStartableModule {
@IntoSet
CentralSurfacesComponent.Startable letterboxAppearanceCalculator(
LetterboxAppearanceCalculator letterboxAppearanceCalculator);
+
+ @Binds
+ @IntoSet
+ CentralSurfacesComponent.Startable sysBarAttrsListener(
+ SystemBarAttributesListener systemBarAttributesListener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index b3bef07cb820..c4e7a8a2c17b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -53,10 +53,12 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll
import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
+import com.android.systemui.statusbar.phone.StatusBarBoundsProvider;
import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
import com.android.systemui.statusbar.phone.StatusIconContainer;
+import com.android.systemui.statusbar.phone.SystemBarAttributesListener;
import com.android.systemui.statusbar.phone.TapAgainView;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentLogger;
@@ -258,6 +260,11 @@ public abstract class StatusBarViewModule {
LetterboxAppearanceCalculator letterboxAppearanceCalculator
);
+ @Binds
+ @IntoSet
+ abstract StatusBarBoundsProvider.BoundsChangeListener sysBarAttrsListenerAsBoundsListener(
+ SystemBarAttributesListener systemBarAttributesListener);
+
/**
* Creates a new {@link CollapsedStatusBarFragment}.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
new file mode 100644
index 000000000000..bd6cf9a1d101
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/**
+ * A processor that transforms raw connectivity information that we get from callbacks and turns it
+ * into a list of displayable connectivity information.
+ *
+ * This will be used for the new status bar pipeline to calculate the list of icons that should be
+ * displayed in the RHS of the status bar.
+ */
+@SysUISingleton
+class ConnectivityInfoProcessor @Inject constructor()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
new file mode 100644
index 000000000000..734bd2d8e5b3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.dagger
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.statusbar.pipeline.ConnectivityInfoProcessor
+import dagger.Lazy
+import dagger.Module
+import dagger.Provides
+import java.util.Optional
+
+@Module
+class StatusBarPipelineModule {
+ @Provides
+ @SysUISingleton
+ fun provideConnectivityInfoProcessor(
+ featureFlags: FeatureFlags,
+ processorLazy: Lazy<ConnectivityInfoProcessor>
+ ): Optional<ConnectivityInfoProcessor> {
+ return if (featureFlags.isEnabled(Flags.NEW_STATUS_BAR_PIPELINE)) {
+ Optional.of(processorLazy.get())
+ } else {
+ Optional.empty()
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 5db2cf49636c..8b9a1e022d26 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -51,6 +51,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.clocks.AnimatableClockView;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 8717a0eaf57f..6c6f0acd7085 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -43,6 +43,7 @@ import android.widget.TextClock;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ClockPlugin;
+import com.android.systemui.shared.clocks.AnimatableClockView;
import com.android.systemui.statusbar.StatusBarState;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index 70025230fa83..4dcaa7cf8c09 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -89,28 +89,6 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase {
}
@Test
- public void timeFormatUpdateNotifiesClockSwitchController() {
- mController.onViewAttached();
-
- verify(mKeyguardUpdateMonitor).registerCallback(
- mKeyguardUpdateMonitorCallbackCaptor.capture());
-
- mKeyguardUpdateMonitorCallbackCaptor.getValue().onTimeFormatChanged("");
- verify(mKeyguardClockSwitchController).refreshFormat();
- }
-
- @Test
- public void userChangeNotifiesClockSwitchController() {
- mController.onViewAttached();
-
- verify(mKeyguardUpdateMonitor).registerCallback(
- mKeyguardUpdateMonitorCallbackCaptor.capture());
-
- mKeyguardUpdateMonitorCallbackCaptor.getValue().onUserSwitchComplete(0);
- verify(mKeyguardClockSwitchController).refreshFormat();
- }
-
- @Test
public void setTranslationYExcludingMedia_forwardsCallToView() {
float translationY = 123f;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/FontInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
index 95fa3b9c594b..f01da2dd3a6b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/FontInterpolatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.keyguard
+package com.android.systemui.animation
import android.graphics.Paint
import android.graphics.fonts.Font
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt
index 3322414e9e7a..ed0cd7ed9b24 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.keyguard
+package com.android.systemui.animation
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
@@ -26,18 +26,17 @@ import android.text.TextPaint
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
+import kotlin.math.ceil
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.eq
import org.mockito.Mockito.inOrder
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-
-import kotlin.math.ceil
+import org.mockito.Mockito.`when`
private val PAINT = TextPaint().apply {
textSize = 32f
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt
index 603cf3bcef74..2a183bd43f83 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.keyguard
+package com.android.systemui.animation
import android.graphics.Bitmap
import android.graphics.Canvas
@@ -31,10 +31,10 @@ import android.text.TextPaint
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
import java.io.File
import kotlin.math.ceil
+import org.junit.Test
+import org.junit.runner.RunWith
private const val TEXT = "Hello, World."
private const val BIDI_TEXT = "abc\u05D0\u05D1\u05D2"
@@ -323,4 +323,4 @@ private fun Layout.toBitmap(width: Int, height: Int) =
Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) }!!
private fun TextInterpolator.toBitmap(width: Int, height: Int) =
- Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) } \ No newline at end of file
+ Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java
index 5a4bb86dba45..df506b479b88 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java
@@ -34,12 +34,12 @@ import android.view.View;
import androidx.test.filters.SmallTest;
import com.android.keyguard.AnimatableClockController;
-import com.android.keyguard.AnimatableClockView;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.Utils;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.clocks.AnimatableClockView;
import com.android.systemui.statusbar.policy.BatteryController;
import org.junit.After;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
index 214ba16dfc44..64d025628754 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
@@ -98,7 +98,7 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() {
@Test
fun testPrepareDialogForApp_onlyDefaultChannel() {
- group.channels = listOf(channelDefault)
+ group.addChannel(channelDefault)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
setOf(channelDefault), appIcon, clickListener)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index d79f3361408b..7b7a48be5b17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -22,26 +22,28 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.app.StatusBarManager;
import android.os.PowerManager;
import android.os.Vibrator;
import android.testing.AndroidTestingRunner;
+import android.view.InsetsVisibilities;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.testing.FakeMetricsLogger;
+import com.android.internal.statusbar.LetterboxDetails;
+import com.android.internal.view.AppearanceRegion;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.shade.NotificationPanelViewController;
-import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DisableFlagsLogger;
-import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -60,6 +62,7 @@ import java.util.Optional;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
+
@Mock private CentralSurfaces mCentralSurfaces;
@Mock private ShadeController mShadeController;
@Mock private CommandQueue mCommandQueue;
@@ -74,14 +77,12 @@ public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock private AssistManager mAssistManager;
@Mock private DozeServiceHost mDozeServiceHost;
- @Mock private StatusBarStateControllerImpl mStatusBarStateController;
- @Mock private NotificationShadeWindowView mNotificationShadeWindowView;
@Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
@Mock private PowerManager mPowerManager;
@Mock private VibratorHelper mVibratorHelper;
@Mock private Vibrator mVibrator;
- @Mock private LightBarController mLightBarController;
@Mock private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
+ @Mock private SystemBarAttributesListener mSystemBarAttributesListener;
CentralSurfacesCommandQueueCallbacks mSbcqCallbacks;
@@ -106,16 +107,14 @@ public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
mStatusBarKeyguardViewManager,
mAssistManager,
mDozeServiceHost,
- mStatusBarStateController,
- mNotificationShadeWindowView,
mNotificationStackScrollLayoutController,
mStatusBarHideIconsForBouncerManager,
mPowerManager,
mVibratorHelper,
Optional.of(mVibrator),
- mLightBarController,
new DisableFlagsLogger(),
- DEFAULT_DISPLAY);
+ DEFAULT_DISPLAY,
+ mSystemBarAttributesListener);
when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
when(mRemoteInputQuickSettingsDisabler.adjustDisableFlags(anyInt()))
@@ -170,5 +169,59 @@ public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
verify(mDozeServiceHost).setAlwaysOnSuppressed(false);
}
+ @Test
+ public void onSystemBarAttributesChanged_forwardsToSysBarAttrsListener() {
+ int displayId = DEFAULT_DISPLAY;
+ int appearance = 123;
+ AppearanceRegion[] appearanceRegions = new AppearanceRegion[]{};
+ boolean navbarColorManagedByIme = true;
+ int behavior = 456;
+ InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
+ String packageName = "test package name";
+ LetterboxDetails[] letterboxDetails = new LetterboxDetails[]{};
+
+ mSbcqCallbacks.onSystemBarAttributesChanged(
+ displayId,
+ appearance,
+ appearanceRegions,
+ navbarColorManagedByIme,
+ behavior,
+ requestedVisibilities,
+ packageName,
+ letterboxDetails);
+
+ verify(mSystemBarAttributesListener).onSystemBarAttributesChanged(
+ displayId,
+ appearance,
+ appearanceRegions,
+ navbarColorManagedByIme,
+ behavior,
+ requestedVisibilities,
+ packageName,
+ letterboxDetails
+ );
+ }
+ @Test
+ public void onSystemBarAttributesChanged_differentDisplayId_doesNotForwardToAttrsListener() {
+ int appearance = 123;
+ AppearanceRegion[] appearanceRegions = new AppearanceRegion[]{};
+ boolean navbarColorManagedByIme = true;
+ int behavior = 456;
+ InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
+ String packageName = "test package name";
+ LetterboxDetails[] letterboxDetails = new LetterboxDetails[]{};
+
+ mSbcqCallbacks.onSystemBarAttributesChanged(
+ DEFAULT_DISPLAY + 1,
+ appearance,
+ appearanceRegions,
+ navbarColorManagedByIme,
+ behavior,
+ requestedVisibilities,
+ packageName,
+ letterboxDetails);
+
+ verifyZeroInteractions(mSystemBarAttributesListener);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemBarAttributesListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemBarAttributesListenerTest.kt
new file mode 100644
index 000000000000..fa7b2599c108
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemBarAttributesListenerTest.kt
@@ -0,0 +1,250 @@
+package com.android.systemui.statusbar.phone
+
+import android.graphics.Rect
+import android.testing.AndroidTestingRunner
+import android.view.Display
+import android.view.InsetsVisibilities
+import android.view.WindowInsetsController
+import android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS
+import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
+import android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS
+import android.view.WindowInsetsController.Appearance
+import androidx.test.filters.SmallTest
+import com.android.internal.statusbar.LetterboxDetails
+import com.android.internal.view.AppearanceRegion
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.statusbar.SysuiStatusBarStateController
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class SystemBarAttributesListenerTest : SysuiTestCase() {
+
+ @Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var lightBarController: LightBarController
+ @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
+ @Mock private lateinit var letterboxAppearanceCalculator: LetterboxAppearanceCalculator
+ @Mock private lateinit var featureFlags: FeatureFlags
+ @Mock private lateinit var centralSurfaces: CentralSurfaces
+
+ private lateinit var sysBarAttrsListener: SystemBarAttributesListener
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ whenever(
+ letterboxAppearanceCalculator.getLetterboxAppearance(
+ anyInt(), anyObject(), anyObject()))
+ .thenReturn(TEST_LETTERBOX_APPEARANCE)
+
+ sysBarAttrsListener =
+ SystemBarAttributesListener(
+ centralSurfaces,
+ featureFlags,
+ letterboxAppearanceCalculator,
+ statusBarStateController,
+ lightBarController,
+ dumpManager)
+ }
+
+ @Test
+ fun onSysBarAttrsChanged_forwardsAppearanceToCentralSurfaces() {
+ val appearance = APPEARANCE_LIGHT_STATUS_BARS or APPEARANCE_LIGHT_NAVIGATION_BARS
+
+ changeSysBarAttrs(appearance)
+
+ verify(centralSurfaces).setAppearance(appearance)
+ }
+
+ @Test
+ fun onSysBarAttrsChanged_flagTrue_forwardsLetterboxAppearanceToCentralSurfaces() {
+ whenever(featureFlags.isEnabled(Flags.STATUS_BAR_LETTERBOX_APPEARANCE)).thenReturn(true)
+
+ changeSysBarAttrs(TEST_APPEARANCE, TEST_LETTERBOX_DETAILS)
+
+ verify(centralSurfaces).setAppearance(TEST_LETTERBOX_APPEARANCE.appearance)
+ }
+
+ @Test
+ fun onSysBarAttrsChanged_flagTrue_noLetterbox_forwardsOriginalAppearanceToCtrlSrfcs() {
+ whenever(featureFlags.isEnabled(Flags.STATUS_BAR_LETTERBOX_APPEARANCE)).thenReturn(true)
+
+ changeSysBarAttrs(TEST_APPEARANCE, arrayOf<LetterboxDetails>())
+
+ verify(centralSurfaces).setAppearance(TEST_APPEARANCE)
+ }
+
+ @Test
+ fun onSysBarAttrsChanged_forwardsAppearanceToStatusBarStateController() {
+ changeSysBarAttrs(TEST_APPEARANCE)
+
+ verify(statusBarStateController)
+ .setSystemBarAttributes(eq(TEST_APPEARANCE), anyInt(), any(), any())
+ }
+
+ @Test
+ fun onSysBarAttrsChanged_flagTrue_forwardsLetterboxAppearanceToStatusBarStateCtrl() {
+ whenever(featureFlags.isEnabled(Flags.STATUS_BAR_LETTERBOX_APPEARANCE)).thenReturn(true)
+
+ changeSysBarAttrs(TEST_APPEARANCE, TEST_LETTERBOX_DETAILS)
+
+ verify(statusBarStateController)
+ .setSystemBarAttributes(
+ eq(TEST_LETTERBOX_APPEARANCE.appearance), anyInt(), any(), any())
+ }
+
+ @Test
+ fun onSysBarAttrsChanged_forwardsAppearanceToLightBarController() {
+ changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS)
+
+ verify(lightBarController)
+ .onStatusBarAppearanceChanged(
+ eq(TEST_APPEARANCE_REGIONS), anyBoolean(), anyInt(), anyBoolean())
+ }
+
+ @Test
+ fun onSysBarAttrsChanged_flagTrue_forwardsLetterboxAppearanceToLightBarController() {
+ whenever(featureFlags.isEnabled(Flags.STATUS_BAR_LETTERBOX_APPEARANCE)).thenReturn(true)
+
+ changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, TEST_LETTERBOX_DETAILS)
+
+ verify(lightBarController)
+ .onStatusBarAppearanceChanged(
+ eq(TEST_LETTERBOX_APPEARANCE.appearanceRegions),
+ anyBoolean(),
+ anyInt(),
+ anyBoolean())
+ }
+
+ @Test
+ fun onStatusBarBoundsChanged_forwardsLetterboxAppearanceToStatusBarStateController() {
+ whenever(featureFlags.isEnabled(Flags.STATUS_BAR_LETTERBOX_APPEARANCE)).thenReturn(true)
+ changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, TEST_LETTERBOX_DETAILS)
+ reset(centralSurfaces, lightBarController, statusBarStateController)
+
+ sysBarAttrsListener.onStatusBarBoundsChanged()
+
+ verify(statusBarStateController)
+ .setSystemBarAttributes(
+ eq(TEST_LETTERBOX_APPEARANCE.appearance), anyInt(), any(), any())
+ }
+
+ @Test
+ fun onStatusBarBoundsChanged_forwardsLetterboxAppearanceToLightBarController() {
+ whenever(featureFlags.isEnabled(Flags.STATUS_BAR_LETTERBOX_APPEARANCE)).thenReturn(true)
+ changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, TEST_LETTERBOX_DETAILS)
+ reset(centralSurfaces, lightBarController, statusBarStateController)
+
+ sysBarAttrsListener.onStatusBarBoundsChanged()
+
+ verify(lightBarController)
+ .onStatusBarAppearanceChanged(
+ eq(TEST_LETTERBOX_APPEARANCE.appearanceRegions),
+ anyBoolean(),
+ anyInt(),
+ anyBoolean())
+ }
+
+ @Test
+ fun onStatusBarBoundsChanged_forwardsLetterboxAppearanceToCentralSurfaces() {
+ whenever(featureFlags.isEnabled(Flags.STATUS_BAR_LETTERBOX_APPEARANCE)).thenReturn(true)
+ changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, TEST_LETTERBOX_DETAILS)
+ reset(centralSurfaces, lightBarController, statusBarStateController)
+
+ sysBarAttrsListener.onStatusBarBoundsChanged()
+
+ verify(centralSurfaces).setAppearance(TEST_LETTERBOX_APPEARANCE.appearance)
+ }
+
+ @Test
+ fun onStatusBarBoundsChanged_previousCallEmptyLetterbox_doesNothing() {
+ whenever(featureFlags.isEnabled(Flags.STATUS_BAR_LETTERBOX_APPEARANCE)).thenReturn(true)
+ changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf())
+ reset(centralSurfaces, lightBarController, statusBarStateController)
+
+ sysBarAttrsListener.onStatusBarBoundsChanged()
+
+ verifyZeroInteractions(centralSurfaces, lightBarController, statusBarStateController)
+ }
+
+ @Test
+ fun onStatusBarBoundsChanged_flagFalse_doesNothing() {
+ whenever(featureFlags.isEnabled(Flags.STATUS_BAR_LETTERBOX_APPEARANCE)).thenReturn(false)
+ changeSysBarAttrs(TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, TEST_LETTERBOX_DETAILS)
+ reset(centralSurfaces, lightBarController, statusBarStateController)
+
+ sysBarAttrsListener.onStatusBarBoundsChanged()
+
+ verifyZeroInteractions(centralSurfaces, lightBarController, statusBarStateController)
+ }
+
+ private fun changeSysBarAttrs(@Appearance appearance: Int) {
+ changeSysBarAttrs(appearance, arrayOf<LetterboxDetails>())
+ }
+
+ private fun changeSysBarAttrs(
+ @Appearance appearance: Int,
+ letterboxDetails: Array<LetterboxDetails>
+ ) {
+ changeSysBarAttrs(appearance, arrayOf(), letterboxDetails)
+ }
+
+ private fun changeSysBarAttrs(
+ @Appearance appearance: Int,
+ appearanceRegions: Array<AppearanceRegion>
+ ) {
+ changeSysBarAttrs(appearance, appearanceRegions, arrayOf())
+ }
+
+ private fun changeSysBarAttrs(
+ @Appearance appearance: Int,
+ appearanceRegions: Array<AppearanceRegion>,
+ letterboxDetails: Array<LetterboxDetails>
+ ) {
+ sysBarAttrsListener.onSystemBarAttributesChanged(
+ Display.DEFAULT_DISPLAY,
+ appearance,
+ appearanceRegions,
+ /* navbarColorManagedByIme= */ false,
+ WindowInsetsController.BEHAVIOR_DEFAULT,
+ InsetsVisibilities(),
+ "package name",
+ letterboxDetails)
+ }
+
+ companion object {
+ private const val TEST_APPEARANCE =
+ APPEARANCE_LIGHT_STATUS_BARS or APPEARANCE_LIGHT_NAVIGATION_BARS
+ private val TEST_APPEARANCE_REGION = AppearanceRegion(TEST_APPEARANCE, Rect(0, 0, 150, 300))
+ private val TEST_APPEARANCE_REGIONS = arrayOf(TEST_APPEARANCE_REGION)
+ private val TEST_LETTERBOX_DETAILS =
+ arrayOf(
+ LetterboxDetails(
+ /* letterboxInnerBounds= */ Rect(0, 0, 0, 0),
+ /* letterboxFullBounds= */ Rect(0, 0, 0, 0),
+ /* appAppearance= */ 0))
+ private val TEST_LETTERBOX_APPEARANCE =
+ LetterboxAppearance(/* appearance= */ APPEARANCE_LOW_PROFILE_BARS, arrayOf())
+ }
+}
+
+private fun <T> anyObject(): T {
+ return Mockito.anyObject<T>()
+}
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index 6076eb1f69ee..7a09ce722f94 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -2790,13 +2790,6 @@ public final class AppRestrictionController {
if (isOnSystemDeviceIdleAllowlist(uid)) {
return REASON_SYSTEM_ALLOW_LISTED;
}
- if (isOnDeviceIdleAllowlist(uid)) {
- return REASON_ALLOWLISTED_PACKAGE;
- }
- final ActivityManagerInternal am = mInjector.getActivityManagerInternal();
- if (am.isAssociatedCompanionApp(UserHandle.getUserId(uid), uid)) {
- return REASON_COMPANION_DEVICE_MANAGER;
- }
if (UserManager.isDeviceInDemoMode(mContext)) {
return REASON_DEVICE_DEMO_MODE;
}
@@ -2805,6 +2798,7 @@ public final class AppRestrictionController {
.hasUserRestriction(UserManager.DISALLOW_APPS_CONTROL, userId)) {
return REASON_DISALLOW_APPS_CONTROL;
}
+ final ActivityManagerInternal am = mInjector.getActivityManagerInternal();
if (am.isDeviceOwner(uid)) {
return REASON_DEVICE_OWNER;
}
@@ -2822,14 +2816,9 @@ public final class AppRestrictionController {
final AppOpsManager appOpsManager = mInjector.getAppOpsManager();
final PackageManagerInternal pm = mInjector.getPackageManagerInternal();
final AppStandbyInternal appStandbyInternal = mInjector.getAppStandbyInternal();
+ // Check each packages to see if any of them is in the "fixed" exemption cases.
for (String pkg : packages) {
- if (appOpsManager.checkOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN,
- uid, pkg) == AppOpsManager.MODE_ALLOWED) {
- return REASON_OP_ACTIVATE_VPN;
- } else if (appOpsManager.checkOpNoThrow(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN,
- uid, pkg) == AppOpsManager.MODE_ALLOWED) {
- return REASON_OP_ACTIVATE_PLATFORM_VPN;
- } else if (isSystemModule(pkg)) {
+ if (isSystemModule(pkg)) {
return REASON_SYSTEM_MODULE;
} else if (isCarrierApp(pkg)) {
return REASON_CARRIER_PRIVILEGED_APP;
@@ -2843,6 +2832,16 @@ public final class AppRestrictionController {
return REASON_ACTIVE_DEVICE_ADMIN;
}
}
+ // Loop the packages again, and check the user-configurable exemptions.
+ for (String pkg : packages) {
+ if (appOpsManager.checkOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN,
+ uid, pkg) == AppOpsManager.MODE_ALLOWED) {
+ return REASON_OP_ACTIVATE_VPN;
+ } else if (appOpsManager.checkOpNoThrow(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN,
+ uid, pkg) == AppOpsManager.MODE_ALLOWED) {
+ return REASON_OP_ACTIVATE_PLATFORM_VPN;
+ }
+ }
}
if (isRoleHeldByUid(RoleManager.ROLE_DIALER, uid)) {
return REASON_ROLE_DIALER;
@@ -2850,6 +2849,12 @@ public final class AppRestrictionController {
if (isRoleHeldByUid(RoleManager.ROLE_EMERGENCY, uid)) {
return REASON_ROLE_EMERGENCY;
}
+ if (isOnDeviceIdleAllowlist(uid)) {
+ return REASON_ALLOWLISTED_PACKAGE;
+ }
+ if (am.isAssociatedCompanionApp(UserHandle.getUserId(uid), uid)) {
+ return REASON_COMPANION_DEVICE_MANAGER;
+ }
return REASON_DENIED;
}
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index cd5960ffbf32..1def72b2987f 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -46,6 +46,8 @@ import android.util.Log;
import android.util.Pair;
import android.util.SparseIntArray;
+import com.android.internal.annotations.GuardedBy;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -103,10 +105,6 @@ public class SpatializerHelper {
AudioDeviceInfo.TYPE_BLE_BROADCAST
};
- private static final int[] WIRELESS_SPEAKER_TYPES = {
- AudioDeviceInfo.TYPE_BLE_SPEAKER,
- };
-
// Spatializer state machine
private static final int STATE_UNINITIALIZED = 0;
private static final int STATE_NOT_SUPPORTED = 1;
@@ -166,6 +164,7 @@ public class SpatializerHelper {
* List of devices where Spatial Audio is possible. Each device can be enabled or disabled
* (== user choice to use or not)
*/
+ @GuardedBy("this")
private final ArrayList<SADeviceState> mSADevices = new ArrayList<>(0);
//------------------------------------------------------
@@ -520,30 +519,30 @@ public class SpatializerHelper {
* set to true if the device is added to the list, otherwise, if already
* present, the setting is left untouched.
*/
+ @GuardedBy("this")
private void addCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada,
boolean forceEnable) {
if (!isDeviceCompatibleWithSpatializationModes(ada)) {
return;
}
loglogi("addCompatibleAudioDevice: dev=" + ada);
- boolean isInList = false;
+ final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
SADeviceState deviceUpdated = null; // non-null on update.
-
- for (SADeviceState deviceState : mSADevices) {
- if (deviceState.matchesAudioDeviceAttributes(ada)) {
- isInList = true;
- if (forceEnable) {
- deviceState.mEnabled = true;
- deviceUpdated = deviceState;
- }
- break;
+ if (deviceState != null) {
+ if (forceEnable && !deviceState.mEnabled) {
+ deviceUpdated = deviceState;
+ deviceUpdated.mEnabled = true;
}
- }
- if (!isInList) {
- final SADeviceState deviceState = new SADeviceState(ada.getType(), ada.getAddress());
- deviceState.mEnabled = true;
- mSADevices.add(deviceState);
- deviceUpdated = deviceState;
+ } else {
+ // When adding, force the device type to be a canonical one.
+ final int canonicalDeviceType = getCanonicalDeviceType(ada.getType());
+ if (canonicalDeviceType == AudioDeviceInfo.TYPE_UNKNOWN) {
+ Log.e(TAG, "addCompatibleAudioDevice with incompatible AudioDeviceAttributes "
+ + ada);
+ return;
+ }
+ deviceUpdated = new SADeviceState(canonicalDeviceType, ada.getAddress());
+ mSADevices.add(deviceUpdated);
}
if (deviceUpdated != null) {
onRoutingUpdated();
@@ -574,90 +573,95 @@ public class SpatializerHelper {
synchronized void removeCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
loglogi("removeCompatibleAudioDevice: dev=" + ada);
- SADeviceState deviceUpdated = null; // non-null on update.
- for (SADeviceState deviceState : mSADevices) {
- if (deviceState.matchesAudioDeviceAttributes(ada)) {
- deviceState.mEnabled = false;
- deviceUpdated = deviceState;
- break;
- }
- }
- if (deviceUpdated != null) {
+ final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
+ if (deviceState != null && deviceState.mEnabled) {
+ deviceState.mEnabled = false;
onRoutingUpdated();
mAudioService.persistSpatialAudioDeviceSettings();
- logDeviceState(deviceUpdated, "removeCompatibleAudioDevice");
+ logDeviceState(deviceState, "removeCompatibleAudioDevice");
}
}
/**
+ * Returns a possibly aliased device type which is used
+ * for spatial audio settings (or TYPE_UNKNOWN if it doesn't exist).
+ */
+ private static @AudioDeviceInfo.AudioDeviceType int getCanonicalDeviceType(int deviceType) {
+ if (isWireless(deviceType)) return deviceType;
+
+ final int spatMode = SPAT_MODE_FOR_DEVICE_TYPE.get(deviceType, Integer.MIN_VALUE);
+ if (spatMode == SpatializationMode.SPATIALIZER_TRANSAURAL) {
+ return AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
+ } else if (spatMode == SpatializationMode.SPATIALIZER_BINAURAL) {
+ return AudioDeviceInfo.TYPE_WIRED_HEADPHONES;
+ }
+ return AudioDeviceInfo.TYPE_UNKNOWN;
+ }
+
+ /**
+ * Returns the Spatial Audio device state for an audio device attributes
+ * or null if it does not exist.
+ */
+ @GuardedBy("this")
+ @Nullable
+ private SADeviceState findDeviceStateForAudioDeviceAttributes(AudioDeviceAttributes ada) {
+ final int deviceType = ada.getType();
+ final boolean isWireless = isWireless(deviceType);
+ final int canonicalDeviceType = getCanonicalDeviceType(deviceType);
+
+ for (SADeviceState deviceState : mSADevices) {
+ if (deviceState.mDeviceType == canonicalDeviceType
+ && (!isWireless || ada.getAddress().equals(deviceState.mDeviceAddress))) {
+ return deviceState;
+ }
+ }
+ return null;
+ }
+
+ /**
* Return if Spatial Audio is enabled and available for the given device
* @param ada
* @return a pair of boolean, 1/ enabled? 2/ available?
*/
private synchronized Pair<Boolean, Boolean> evaluateState(AudioDeviceAttributes ada) {
- // if not a wireless device, this value will be overwritten to map the type
- // to TYPE_BUILTIN_SPEAKER or TYPE_WIRED_HEADPHONES
- @AudioDeviceInfo.AudioDeviceType int deviceType = ada.getType();
-
- // if not a wireless device: find if media device is in the speaker, wired headphones
- if (!isWireless(deviceType)) {
- // is the device type capable of doing SA?
- if (!mSACapableDeviceTypes.contains(deviceType)) {
- Log.i(TAG, "Device incompatible with Spatial Audio dev:" + ada);
- return new Pair<>(false, false);
- }
- // what spatialization mode to use for this device?
- final int spatMode = SPAT_MODE_FOR_DEVICE_TYPE.get(deviceType, Integer.MIN_VALUE);
- if (spatMode == Integer.MIN_VALUE) {
- // error case, device not found
- Log.e(TAG, "no spatialization mode found for device type:" + deviceType);
- return new Pair<>(false, false);
- }
- // map the spatialization mode to the SPEAKER or HEADPHONES device
- if (spatMode == SpatializationMode.SPATIALIZER_TRANSAURAL) {
- deviceType = AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
- } else {
- deviceType = AudioDeviceInfo.TYPE_WIRED_HEADPHONES;
- }
- } else { // wireless device
- if (isWirelessSpeaker(deviceType) && !mTransauralSupported) {
- Log.i(TAG, "Device incompatible with Spatial Audio (no transaural) dev:"
- + ada);
- return new Pair<>(false, false);
- }
- if (!mBinauralSupported) {
- Log.i(TAG, "Device incompatible with Spatial Audio (no binaural) dev:"
- + ada);
- return new Pair<>(false, false);
- }
+ final @AudioDeviceInfo.AudioDeviceType int deviceType = ada.getType();
+ // is the device type capable of doing SA?
+ if (!mSACapableDeviceTypes.contains(deviceType)) {
+ Log.i(TAG, "Device incompatible with Spatial Audio dev:" + ada);
+ return new Pair<>(false, false);
}
-
- boolean enabled = false;
- boolean available = false;
- for (SADeviceState deviceState : mSADevices) {
- if (deviceState.matchesAudioDeviceAttributes(ada)) {
- available = true;
- enabled = deviceState.mEnabled;
- break;
- }
+ // what spatialization mode to use for this device?
+ final int spatMode = SPAT_MODE_FOR_DEVICE_TYPE.get(deviceType, Integer.MIN_VALUE);
+ if (spatMode == Integer.MIN_VALUE) {
+ // error case, device not found
+ Log.e(TAG, "no spatialization mode found for device type:" + deviceType);
+ return new Pair<>(false, false);
+ }
+ final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
+ if (deviceState == null) {
+ // no matching device state?
+ Log.i(TAG, "no spatialization device state found for Spatial Audio device:" + ada);
+ return new Pair<>(false, false);
}
- return new Pair<>(enabled, available);
+ // found the matching device state.
+ return new Pair<>(deviceState.mEnabled, true /* available */);
}
private synchronized void addWirelessDeviceIfNew(@NonNull AudioDeviceAttributes ada) {
if (!isDeviceCompatibleWithSpatializationModes(ada)) {
return;
}
- boolean knownDevice = false;
- for (SADeviceState deviceState : mSADevices) {
- if (deviceState.matchesAudioDeviceAttributes(ada)) {
- knownDevice = true;
- break;
+ if (findDeviceStateForAudioDeviceAttributes(ada) == null) {
+ // wireless device types should be canonical, but we translate to be sure.
+ final int canonicalDeviceType = getCanonicalDeviceType((ada.getType()));
+ if (canonicalDeviceType == AudioDeviceInfo.TYPE_UNKNOWN) {
+ Log.e(TAG, "addWirelessDeviceIfNew with incompatible AudioDeviceAttributes "
+ + ada);
+ return;
}
- }
- if (!knownDevice) {
- final SADeviceState deviceState = new SADeviceState(ada.getType(), ada.getAddress());
+ final SADeviceState deviceState =
+ new SADeviceState(canonicalDeviceType, ada.getAddress());
mSADevices.add(deviceState);
mAudioService.persistSpatialAudioDeviceSettings();
logDeviceState(deviceState, "addWirelessDeviceIfNew"); // may be updated later.
@@ -699,12 +703,7 @@ public class SpatializerHelper {
if (ada.getRole() != AudioDeviceAttributes.ROLE_OUTPUT) {
return false;
}
- for (SADeviceState deviceState : mSADevices) {
- if (deviceState.matchesAudioDeviceAttributes(ada)) {
- return true;
- }
- }
- return false;
+ return findDeviceStateForAudioDeviceAttributes(ada) != null;
}
private synchronized boolean canBeSpatializedOnDevice(@NonNull AudioAttributes attributes,
@@ -1086,20 +1085,18 @@ public class SpatializerHelper {
Log.v(TAG, "no headtracking support, ignoring setHeadTrackerEnabled to " + enabled
+ " for " + ada);
}
- for (SADeviceState deviceState : mSADevices) {
- if (deviceState.matchesAudioDeviceAttributes(ada)) {
- if (!deviceState.mHasHeadTracker) {
- Log.e(TAG, "Called setHeadTrackerEnabled enabled:" + enabled
- + " device:" + ada + " on a device without headtracker");
- return;
- }
- Log.i(TAG, "setHeadTrackerEnabled enabled:" + enabled + " device:" + ada);
- deviceState.mHeadTrackerEnabled = enabled;
- mAudioService.persistSpatialAudioDeviceSettings();
- logDeviceState(deviceState, "setHeadTrackerEnabled");
- break;
- }
+ final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
+ if (deviceState == null) return;
+ if (!deviceState.mHasHeadTracker) {
+ Log.e(TAG, "Called setHeadTrackerEnabled enabled:" + enabled
+ + " device:" + ada + " on a device without headtracker");
+ return;
}
+ Log.i(TAG, "setHeadTrackerEnabled enabled:" + enabled + " device:" + ada);
+ deviceState.mHeadTrackerEnabled = enabled;
+ mAudioService.persistSpatialAudioDeviceSettings();
+ logDeviceState(deviceState, "setHeadTrackerEnabled");
+
// check current routing to see if it affects the headtracking mode
if (ROUTING_DEVICES[0].getType() == ada.getType()
&& ROUTING_DEVICES[0].getAddress().equals(ada.getAddress())) {
@@ -1113,12 +1110,8 @@ public class SpatializerHelper {
Log.v(TAG, "no headtracking support, hasHeadTracker always false for " + ada);
return false;
}
- for (SADeviceState deviceState : mSADevices) {
- if (deviceState.matchesAudioDeviceAttributes(ada)) {
- return deviceState.mHasHeadTracker;
- }
- }
- return false;
+ final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
+ return deviceState != null && deviceState.mHasHeadTracker;
}
/**
@@ -1131,15 +1124,14 @@ public class SpatializerHelper {
Log.v(TAG, "no headtracking support, setHasHeadTracker always false for " + ada);
return false;
}
- for (SADeviceState deviceState : mSADevices) {
- if (deviceState.matchesAudioDeviceAttributes(ada)) {
- if (!deviceState.mHasHeadTracker) {
- deviceState.mHasHeadTracker = true;
- mAudioService.persistSpatialAudioDeviceSettings();
- logDeviceState(deviceState, "setHasHeadTracker");
- }
- return deviceState.mHeadTrackerEnabled;
+ final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
+ if (deviceState != null) {
+ if (!deviceState.mHasHeadTracker) {
+ deviceState.mHasHeadTracker = true;
+ mAudioService.persistSpatialAudioDeviceSettings();
+ logDeviceState(deviceState, "setHasHeadTracker");
}
+ return deviceState.mHeadTrackerEnabled;
}
Log.e(TAG, "setHasHeadTracker: device not found for:" + ada);
return false;
@@ -1150,15 +1142,9 @@ public class SpatializerHelper {
Log.v(TAG, "no headtracking support, isHeadTrackerEnabled always false for " + ada);
return false;
}
- for (SADeviceState deviceState : mSADevices) {
- if (deviceState.matchesAudioDeviceAttributes(ada)) {
- if (!deviceState.mHasHeadTracker) {
- return false;
- }
- return deviceState.mHeadTrackerEnabled;
- }
- }
- return false;
+ final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
+ return deviceState != null
+ && deviceState.mHasHeadTracker && deviceState.mHeadTrackerEnabled;
}
synchronized boolean isHeadTrackerAvailable() {
@@ -1582,12 +1568,6 @@ public class SpatializerHelper {
mDeviceType, mDeviceAddress == null ? "" : mDeviceAddress);
}
- public boolean matchesAudioDeviceAttributes(AudioDeviceAttributes ada) {
- final int deviceType = ada.getType();
- final boolean wireless = isWireless(deviceType);
- return (deviceType == mDeviceType)
- && (!wireless || ada.getAddress().equals(mDeviceAddress));
- }
}
/*package*/ synchronized String getSADeviceSettings() {
@@ -1608,7 +1588,10 @@ public class SpatializerHelper {
// small list, not worth overhead of Arrays.stream(devSettings)
for (String setting : devSettings) {
SADeviceState devState = SADeviceState.fromPersistedString(setting);
+ // Note if the device is not compatible with spatialization mode
+ // or the device type is not canonical, it is ignored.
if (devState != null
+ && devState.mDeviceType == getCanonicalDeviceType(devState.mDeviceType)
&& isDeviceCompatibleWithSpatializationModes(
devState.getAudioDeviceAttributes())) {
mSADevices.add(devState);
@@ -1645,15 +1628,6 @@ public class SpatializerHelper {
return false;
}
- private static boolean isWirelessSpeaker(@AudioDeviceInfo.AudioDeviceType int deviceType) {
- for (int type : WIRELESS_SPEAKER_TYPES) {
- if (type == deviceType) {
- return true;
- }
- }
- return false;
- }
-
private int getHeadSensorHandleUpdateTracker() {
int headHandle = -1;
UUID routingDeviceUuid = mAudioService.getDeviceSensorUuid(ROUTING_DEVICES[0]);
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java b/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java
index 1b24aa896652..0d789f7a1840 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java
@@ -108,6 +108,17 @@ public class BiometricStateCallback implements ClientMonitorCallback {
}
}
+ @Override
+ public void onBiometricAction(@BiometricStateListener.Action int action) {
+ for (IBiometricStateListener listener : mBiometricStateListeners) {
+ try {
+ listener.onBiometricAction(action);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception in onBiometricAction", e);
+ }
+ }
+ }
+
/**
* This should be invoked when:
* 1) Enrolled --> None-enrolled
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallback.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallback.java
index 8ea4ee911cb1..4417f9270d9c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallback.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallback.java
@@ -31,6 +31,11 @@ public interface ClientMonitorCallback {
default void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {}
/**
+ * Invoked when a biometric action has occurred.
+ */
+ default void onBiometricAction(int action) {}
+
+ /**
* Invoked when the ClientMonitor operation is complete. This abstracts away asynchronous
* (i.e. Authenticate, Enroll, Enumerate, Remove) and synchronous (i.e. generateChallenge,
* revokeChallenge) so that a scheduler can process ClientMonitors regardless of their
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCompositeCallback.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCompositeCallback.java
index b82f5fa129d6..07041bf8ce3d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCompositeCallback.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCompositeCallback.java
@@ -44,6 +44,13 @@ public class ClientMonitorCompositeCallback implements ClientMonitorCallback {
}
@Override
+ public final void onBiometricAction(int action) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).onBiometricAction(action);
+ }
+ }
+
+ @Override
public final void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 0c5b19b0fb2e..94b67cedf86c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -1020,9 +1020,18 @@ public class FingerprintService extends SystemService {
@Override
public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
FingerprintService.this.registerBiometricStateListener(listener);
}
- }
+
+ @Override
+ public void onPowerPressed() {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ for (ServiceProvider provider : mServiceProviders) {
+ provider.onPowerPressed();
+ }
+ }
+ };
public FingerprintService(Context context) {
super(context);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/PowerPressHandler.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/PowerPressHandler.java
new file mode 100644
index 000000000000..288c372a3f13
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/PowerPressHandler.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint;
+
+/**
+ * Interface for handling power presses.
+ */
+public interface PowerPressHandler {
+ /**
+ * Indicates a power press has occurred.
+ */
+ void onPowerPressed();
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index 9cdbdc9158fb..24a47e081498 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -150,6 +150,8 @@ public interface ServiceProvider {
void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller);
+ void onPowerPressed();
+
/**
* Sets side-fps controller
* @param controller side-fps controller
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 79e3bf53acd3..e1626f014d8b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.app.TaskStackListener;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcquired;
import android.hardware.biometrics.common.ICancellationSignal;
@@ -29,10 +30,15 @@ import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.Build;
+import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Slog;
+import com.android.internal.R;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.CallbackWithProbe;
@@ -46,17 +52,18 @@ import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutConsumer;
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.SensorOverlays;
+import com.android.server.biometrics.sensors.fingerprint.PowerPressHandler;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
import java.util.ArrayList;
import java.util.function.Supplier;
/**
- * Fingerprint-specific authentication client supporting the
- * {@link android.hardware.biometrics.fingerprint.IFingerprint} AIDL interface.
+ * Fingerprint-specific authentication client supporting the {@link
+ * android.hardware.biometrics.fingerprint.IFingerprint} AIDL interface.
*/
-class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> implements
- Udfps, LockoutConsumer {
+class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
+ implements Udfps, LockoutConsumer, PowerPressHandler {
private static final String TAG = "FingerprintAuthenticationClient";
@NonNull private final LockoutCache mLockoutCache;
@@ -64,33 +71,88 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
@NonNull private final FingerprintSensorPropertiesInternal mSensorProps;
@NonNull private final CallbackWithProbe<Probe> mALSProbeCallback;
- @Nullable private ICancellationSignal mCancellationSignal;
+ @Nullable
+ private ICancellationSignal mCancellationSignal;
private boolean mIsPointerDown;
+ private final Handler mHandler;
- FingerprintAuthenticationClient(@NonNull Context context,
+ private static final int MESSAGE_IGNORE_AUTH = 1;
+ private static final int MESSAGE_AUTH_SUCCESS = 2;
+ private long mWaitForAuthKeyguard;
+ private long mWaitForAuthBp;
+ private long mIgnoreAuthFor;
+
+ FingerprintAuthenticationClient(
+ @NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon,
- @NonNull IBinder token, long requestId,
- @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
- boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
+ @NonNull IBinder token,
+ long requestId,
+ @NonNull ClientMonitorCallbackConverter listener,
+ int targetUserId,
+ long operationId,
+ boolean restricted,
+ @NonNull String owner,
+ int cookie,
+ boolean requireConfirmation,
int sensorId,
- @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
+ @NonNull BiometricLogger biometricLogger,
+ @NonNull BiometricContext biometricContext,
boolean isStrongBiometric,
- @Nullable TaskStackListener taskStackListener, @NonNull LockoutCache lockoutCache,
+ @Nullable TaskStackListener taskStackListener,
+ @NonNull LockoutCache lockoutCache,
@Nullable IUdfpsOverlayController udfpsOverlayController,
@Nullable ISidefpsController sidefpsController,
boolean allowBackgroundAuthentication,
- @NonNull FingerprintSensorPropertiesInternal sensorProps) {
- super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, owner,
- cookie, requireConfirmation, sensorId,
- biometricLogger, biometricContext,
- isStrongBiometric, taskStackListener,
- lockoutCache, allowBackgroundAuthentication, true /* shouldVibrate */,
+ @NonNull FingerprintSensorPropertiesInternal sensorProps,
+ @NonNull Handler handler) {
+ super(
+ context,
+ lazyDaemon,
+ token,
+ listener,
+ targetUserId,
+ operationId,
+ restricted,
+ owner,
+ cookie,
+ requireConfirmation,
+ sensorId,
+ biometricLogger,
+ biometricContext,
+ isStrongBiometric,
+ taskStackListener,
+ lockoutCache,
+ allowBackgroundAuthentication,
+ true /* shouldVibrate */,
false /* isKeyguardBypassEnabled */);
setRequestId(requestId);
mLockoutCache = lockoutCache;
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mSensorProps = sensorProps;
mALSProbeCallback = getLogger().createALSCallback(false /* startWithClient */);
+ mHandler = handler;
+
+ mWaitForAuthKeyguard =
+ context.getResources()
+ .getInteger(R.integer.config_sidefpsKeyguardPowerPressWindow);
+ mWaitForAuthBp =
+ context.getResources().getInteger(R.integer.config_sidefpsBpPowerPressWindow);
+ mIgnoreAuthFor =
+ context.getResources().getInteger(R.integer.config_sidefpsPostAuthDowntime);
+
+ if (mSensorProps.isAnySidefpsType()) {
+ if (Build.isDebuggable()) {
+ mWaitForAuthKeyguard = Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.FINGERPRINT_SIDE_FPS_KG_POWER_WINDOW,
+ (int) mWaitForAuthKeyguard, UserHandle.USER_CURRENT);
+ mWaitForAuthBp = Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.FINGERPRINT_SIDE_FPS_BP_POWER_WINDOW, (int) mWaitForAuthBp,
+ UserHandle.USER_CURRENT);
+ mIgnoreAuthFor = Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.FINGERPRINT_SIDE_FPS_AUTH_DOWNTIME, (int) mIgnoreAuthFor,
+ UserHandle.USER_CURRENT);
+ }
+ }
}
@Override
@@ -108,8 +170,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
@NonNull
@Override
protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
- return new ClientMonitorCompositeCallback(mALSProbeCallback,
- getBiometricContextUnsubscriber(), callback);
+ return new ClientMonitorCompositeCallback(
+ mALSProbeCallback, getBiometricContextUnsubscriber(), callback);
}
@Override
@@ -126,16 +188,37 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
}
@Override
- public void onAuthenticated(BiometricAuthenticator.Identifier identifier,
- boolean authenticated, ArrayList<Byte> token) {
- super.onAuthenticated(identifier, authenticated, token);
-
- if (authenticated) {
- mState = STATE_STOPPED;
- mSensorOverlays.hide(getSensorId());
- } else {
- mState = STATE_STARTED_PAUSED_ATTEMPTED;
+ public void onAuthenticated(
+ BiometricAuthenticator.Identifier identifier,
+ boolean authenticated,
+ ArrayList<Byte> token) {
+
+ long delay = 0;
+ if (authenticated && mSensorProps.isAnySidefpsType()) {
+ if (mHandler.hasMessages(MESSAGE_IGNORE_AUTH)) {
+ Slog.i(TAG, "(sideFPS) Ignoring auth due to recent power press");
+ onErrorInternal(BiometricConstants.BIOMETRIC_ERROR_POWER_PRESSED, 0, true);
+ return;
+ }
+ delay = isKeyguard() ? mWaitForAuthKeyguard : mWaitForAuthBp;
+ Slog.i(TAG, "(sideFPS) Auth succeeded, sideFps waiting for power until: " + delay);
}
+
+ mHandler.postDelayed(
+ () -> {
+ if (authenticated && mSensorProps.isAnySidefpsType()) {
+ Slog.i(TAG, "(sideFPS): No power press detected, sending auth");
+ }
+ super.onAuthenticated(identifier, authenticated, token);
+ if (authenticated) {
+ mState = STATE_STOPPED;
+ mSensorOverlays.hide(getSensorId());
+ } else {
+ mState = STATE_STARTED_PAUSED_ATTEMPTED;
+ }
+ },
+ MESSAGE_AUTH_SUCCESS,
+ delay);
}
@Override
@@ -165,7 +248,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
mCancellationSignal = doAuthenticate();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
- onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ onError(
+ BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
mSensorOverlays.hide(getSensorId());
mCallback.onClientFinished(this, false /* success */);
@@ -177,15 +261,18 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
if (session.hasContextMethods()) {
final OperationContext opContext = getOperationContext();
- final ICancellationSignal cancel = session.getSession().authenticateWithContext(
- mOperationId, opContext);
- getBiometricContext().subscribe(opContext, ctx -> {
- try {
- session.getSession().onContextChanged(ctx);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to notify context changed", e);
- }
- });
+ final ICancellationSignal cancel =
+ session.getSession().authenticateWithContext(mOperationId, opContext);
+ getBiometricContext()
+ .subscribe(
+ opContext,
+ ctx -> {
+ try {
+ session.getSession().onContextChanged(ctx);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to notify context changed", e);
+ }
+ });
return cancel;
} else {
return session.getSession().authenticate(mOperationId);
@@ -202,7 +289,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
mCancellationSignal.cancel();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
- onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ onError(
+ BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
mCallback.onClientFinished(this, false /* success */);
}
@@ -284,8 +372,13 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
// Lockout metrics are logged as an error code.
final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
- getLogger().logOnError(getContext(), getOperationContext(),
- error, 0 /* vendorCode */, getTargetUserId());
+ getLogger()
+ .logOnError(
+ getContext(),
+ getOperationContext(),
+ error,
+ 0 /* vendorCode */,
+ getTargetUserId());
try {
getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
@@ -303,8 +396,13 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT);
// Lockout metrics are logged as an error code.
final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
- getLogger().logOnError(getContext(), getOperationContext(),
- error, 0 /* vendorCode */, getTargetUserId());
+ getLogger()
+ .logOnError(
+ getContext(),
+ getOperationContext(),
+ error,
+ 0 /* vendorCode */,
+ getTargetUserId());
try {
getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
@@ -315,4 +413,19 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
mSensorOverlays.hide(getSensorId());
mCallback.onClientFinished(this, false /* success */);
}
+
+ @Override
+ public void onPowerPressed() {
+ if (mSensorProps.isAnySidefpsType()) {
+ Slog.i(TAG, "(sideFPS): onPowerPressed");
+ if (mHandler.hasMessages(MESSAGE_AUTH_SUCCESS)) {
+ Slog.i(TAG, "(sideFPS): Ignoring auth in queue");
+ mHandler.removeMessages(MESSAGE_AUTH_SUCCESS);
+ // Do not call onError() as that will send an additional callback to coex.
+ onErrorInternal(BiometricConstants.BIOMETRIC_ERROR_POWER_PRESSED, 0, true);
+ }
+ mHandler.removeMessages(MESSAGE_IGNORE_AUTH);
+ mHandler.postDelayed(() -> {}, MESSAGE_IGNORE_AUTH, mIgnoreAuthFor);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index f23659c94e49..f4f0a19d0db7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcquired;
+import android.hardware.biometrics.BiometricStateListener;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.fingerprint.PointerContext;
@@ -139,7 +140,7 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps
controller.onEnrollmentHelp(getSensorId());
}
});
-
+ mCallback.onBiometricAction(BiometricStateListener.ACTION_SENSOR_TOUCH);
super.onAcquired(acquiredInfo, vendorCode);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index f16af78364a2..6f6c09b91a66 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -67,6 +67,7 @@ import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
+import com.android.server.biometrics.sensors.fingerprint.PowerPressHandler;
import com.android.server.biometrics.sensors.fingerprint.ServiceProvider;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
@@ -388,6 +389,11 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
}
@Override
+ public void onBiometricAction(int action) {
+ mBiometricStateCallback.onBiometricAction(action);
+ }
+
+ @Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
mBiometricStateCallback.onClientFinished(clientMonitor, success);
@@ -441,7 +447,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mBiometricContext, isStrongBiometric,
mTaskStackListener, mSensors.get(sensorId).getLockoutCache(),
mUdfpsOverlayController, mSidefpsController, allowBackgroundAuthentication,
- mSensors.get(sensorId).getSensorProperties());
+ mSensors.get(sensorId).getSensorProperties(), mHandler);
scheduleForSensor(sensorId, client, mBiometricStateCallback);
});
}
@@ -614,6 +620,21 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
}
@Override
+ public void onPowerPressed() {
+ for (int i = 0; i < mSensors.size(); i++) {
+ final Sensor sensor = mSensors.valueAt(i);
+ BaseClientMonitor client = sensor.getScheduler().getCurrentClient();
+ if (client == null) {
+ return;
+ }
+ if (!(client instanceof PowerPressHandler)) {
+ continue;
+ }
+ ((PowerPressHandler) client).onPowerPressed();
+ }
+ }
+
+ @Override
public void setSidefpsController(@NonNull ISidefpsController controller) {
mSidefpsController = controller;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 2a3f34ae3cd4..c1a86386dfd4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -603,6 +603,11 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
}
@Override
+ public void onBiometricAction(int action) {
+ mBiometricStateCallback.onBiometricAction(action);
+ }
+
+ @Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
mBiometricStateCallback.onClientFinished(clientMonitor, success);
@@ -821,6 +826,11 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
}
@Override
+ public void onPowerPressed() {
+ Slog.e(TAG, "onPowerPressed not supported for HIDL clients");
+ }
+
+ @Override
public void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller) {
mUdfpsOverlayController = controller;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index 1d478e53fd38..2a59c8c0f204 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFingerprintConstants;
+import android.hardware.biometrics.BiometricStateListener;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
@@ -151,6 +152,8 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
controller.onEnrollmentHelp(getSensorId());
}
});
+
+ mCallback.onBiometricAction(BiometricStateListener.ACTION_SENSOR_TOUCH);
}
@Override
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index 9fb1d8e744fb..625f1936e28e 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -871,10 +871,9 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
if (!mPendingIntentRequest.hasPendingIntent() && mRegistered) {
mClientManager.unregisterClient(mHostEndPointId);
mRegistered = false;
+ mAppOpsManager.stopWatchingMode(this);
+ mContextHubProxy.onHostEndpointDisconnected(mHostEndPointId);
}
- mAppOpsManager.stopWatchingMode(this);
-
- mContextHubProxy.onHostEndpointDisconnected(mHostEndPointId);
}
private String authStateToString(@ContextHubManager.AuthorizationState int state) {
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 066692d374d0..477b8da61e0f 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -86,7 +86,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -1328,17 +1327,16 @@ public class PreferencesHelper implements RankingConfig {
return null;
}
NotificationChannelGroup group = r.groups.get(groupId).clone();
- ArrayList channels = new ArrayList();
+ group.setChannels(new ArrayList<>());
int N = r.channels.size();
for (int i = 0; i < N; i++) {
final NotificationChannel nc = r.channels.valueAt(i);
if (includeDeleted || !nc.isDeleted()) {
if (groupId.equals(nc.getGroup())) {
- channels.add(nc);
+ group.addChannel(nc);
}
}
}
- group.setChannels(channels);
return group;
}
}
@@ -1359,48 +1357,44 @@ public class PreferencesHelper implements RankingConfig {
public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty) {
Objects.requireNonNull(pkg);
- List<NotificationChannelGroup> groups = new ArrayList<>();
+ Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
synchronized (mPackagePreferences) {
PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
if (r == null) {
return ParceledListSlice.emptyList();
}
- Map<String, ArrayList<NotificationChannel>> groupedChannels = new HashMap();
+ NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
int N = r.channels.size();
for (int i = 0; i < N; i++) {
final NotificationChannel nc = r.channels.valueAt(i);
if (includeDeleted || !nc.isDeleted()) {
if (nc.getGroup() != null) {
if (r.groups.get(nc.getGroup()) != null) {
- ArrayList<NotificationChannel> channels = groupedChannels.getOrDefault(
- nc.getGroup(), new ArrayList<>());
- channels.add(nc);
- groupedChannels.put(nc.getGroup(), channels);
+ NotificationChannelGroup ncg = groups.get(nc.getGroup());
+ if (ncg == null) {
+ ncg = r.groups.get(nc.getGroup()).clone();
+ ncg.setChannels(new ArrayList<>());
+ groups.put(nc.getGroup(), ncg);
+
+ }
+ ncg.addChannel(nc);
}
} else {
- ArrayList<NotificationChannel> channels = groupedChannels.getOrDefault(
- null, new ArrayList<>());
- channels.add(nc);
- groupedChannels.put(null, channels);
+ nonGrouped.addChannel(nc);
}
}
}
- for (NotificationChannelGroup group : r.groups.values()) {
- ArrayList<NotificationChannel> channels =
- groupedChannels.getOrDefault(group.getId(), new ArrayList<>());
- if (includeEmpty || !channels.isEmpty()) {
- NotificationChannelGroup clone = group.clone();
- clone.setChannels(channels);
- groups.add(clone);
- }
+ if (includeNonGrouped && nonGrouped.getChannels().size() > 0) {
+ groups.put(null, nonGrouped);
}
-
- if (includeNonGrouped && groupedChannels.containsKey(null)) {
- NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
- nonGrouped.setChannels(groupedChannels.get(null));
- groups.add(nonGrouped);
+ if (includeEmpty) {
+ for (NotificationChannelGroup group : r.groups.values()) {
+ if (!groups.containsKey(group.getId())) {
+ groups.put(group.getId(), group);
+ }
+ }
}
- return new ParceledListSlice<>(groups);
+ return new ParceledListSlice<>(new ArrayList<>(groups.values()));
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index d88949bcc0ec..d645bb211bc3 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -949,6 +949,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
private void powerPress(long eventTime, int count, boolean beganFromNonInteractive) {
+ // SideFPS still needs to know about suppressed power buttons, in case it needs to block
+ // an auth attempt.
+ if (count == 1) {
+ mSideFpsEventHandler.notifyPowerPressed();
+ }
if (mDefaultDisplayPolicy.isScreenOnEarly() && !mDefaultDisplayPolicy.isScreenOnFully()) {
Slog.i(TAG, "Suppressed redundant power key press while "
+ "already in the process of turning the screen on.");
@@ -968,7 +973,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
} else if (count > 3 && count <= getMaxMultiPressPowerCount()) {
Slog.d(TAG, "No behavior defined for power press count " + count);
} else if (count == 1 && interactive && !beganFromNonInteractive) {
- if (mSideFpsEventHandler.onSinglePressDetected(eventTime)) {
+ if (mSideFpsEventHandler.shouldConsumeSinglePress(eventTime)) {
Slog.i(TAG, "Suppressing power key because the user is interacting with the "
+ "fingerprint sensor");
return;
diff --git a/services/core/java/com/android/server/policy/SideFpsEventHandler.java b/services/core/java/com/android/server/policy/SideFpsEventHandler.java
index 41d02727d9eb..af2b902c97e8 100644
--- a/services/core/java/com/android/server/policy/SideFpsEventHandler.java
+++ b/services/core/java/com/android/server/policy/SideFpsEventHandler.java
@@ -19,6 +19,7 @@ package com.android.server.policy;
import static android.hardware.biometrics.BiometricStateListener.STATE_BP_AUTH;
import static android.hardware.biometrics.BiometricStateListener.STATE_ENROLLING;
import static android.hardware.biometrics.BiometricStateListener.STATE_IDLE;
+import static android.hardware.biometrics.BiometricStateListener.STATE_KEYGUARD_AUTH;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -34,8 +35,12 @@ import android.hardware.biometrics.BiometricStateListener;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
+import android.os.Build;
import android.os.Handler;
import android.os.PowerManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
import android.view.WindowManager;
import com.android.internal.R;
@@ -46,14 +51,26 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
/**
- * Defines behavior for handling interactions between power button events and
- * fingerprint-related operations, for devices where the fingerprint sensor (side fps)
- * lives on the power button.
+ * Defines behavior for handling interactions between power button events and fingerprint-related
+ * operations, for devices where the fingerprint sensor (side fps) lives on the power button.
*/
public class SideFpsEventHandler {
private static final int DEBOUNCE_DELAY_MILLIS = 500;
+ private int getTapWaitForPowerDuration(Context context) {
+ int tap = context.getResources().getInteger(
+ R.integer.config_sidefpsEnrollPowerPressWindow);
+ if (Build.isDebuggable()) {
+ tap = Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.FINGERPRINT_SIDE_FPS_ENROLL_TAP_WINDOW, tap,
+ UserHandle.USER_CURRENT);
+ }
+ return tap;
+ }
+
+ private static final String TAG = "SideFpsEventHandler";
+
@NonNull private final Context mContext;
@NonNull private final Handler mHandler;
@NonNull private final PowerManager mPowerManager;
@@ -61,20 +78,26 @@ public class SideFpsEventHandler {
@NonNull private final AtomicBoolean mSideFpsEventHandlerReady;
@Nullable private Dialog mDialog;
- @NonNull private final DialogInterface.OnDismissListener mDialogDismissListener = (dialog) -> {
- if (mDialog == dialog) {
- mDialog = null;
- }
- };
+ private final Runnable mTurnOffDialog =
+ () -> {
+ dismissDialog("mTurnOffDialog");
+ };
+
+ @NonNull private final DialogInterface.OnDismissListener mDialogDismissListener;
private @BiometricStateListener.State int mBiometricState;
+ private final int mTapWaitForPowerDuration;
+ private FingerprintManager mFingerprintManager;
SideFpsEventHandler(Context context, Handler handler, PowerManager powerManager) {
this(context, handler, powerManager, () -> new AlertDialog.Builder(context));
}
@VisibleForTesting
- SideFpsEventHandler(Context context, Handler handler, PowerManager powerManager,
+ SideFpsEventHandler(
+ Context context,
+ Handler handler,
+ PowerManager powerManager,
Supplier<AlertDialog.Builder> dialogSupplier) {
mContext = context;
mHandler = handler;
@@ -82,91 +105,131 @@ public class SideFpsEventHandler {
mDialogSupplier = dialogSupplier;
mBiometricState = STATE_IDLE;
mSideFpsEventHandlerReady = new AtomicBoolean(false);
+ mDialogDismissListener =
+ (dialog) -> {
+ if (mDialog == dialog) {
+ if (mHandler != null) {
+ mHandler.removeCallbacks(mTurnOffDialog);
+ }
+ mDialog = null;
+ }
+ };
// ensure dialog is dismissed if screen goes off for unrelated reasons
- context.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (mDialog != null) {
- mDialog.dismiss();
- mDialog = null;
- }
- }
- }, new IntentFilter(Intent.ACTION_SCREEN_OFF));
+ context.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (mDialog != null) {
+ mDialog.dismiss();
+ mDialog = null;
+ }
+ }
+ },
+ new IntentFilter(Intent.ACTION_SCREEN_OFF));
+ mTapWaitForPowerDuration = getTapWaitForPowerDuration(context);
}
/**
- * Called from {@link PhoneWindowManager} after the power button is pressed and displays a
- * dialog confirming the user's intent to turn screen off if a fingerprint operation is
- * active. The device goes to sleep if confirmed otherwise the dialog is dismissed.
+ * Called from {@link PhoneWindowManager} to notify FingerprintManager that a single tap power
+ * button has been pressed.
+ */
+ public void notifyPowerPressed() {
+ Log.i(TAG, "notifyPowerPressed");
+ if (mFingerprintManager == null) {
+ mFingerprintManager = mContext.getSystemService(FingerprintManager.class);
+ }
+ if (mFingerprintManager == null) {
+ return;
+ }
+ mFingerprintManager.onPowerPressed();
+ }
+
+ /**
+ * Called from {@link PhoneWindowManager} and will dictate if the SideFpsEventHandler should
+ * handle the power press.
*
* @param eventTime powerPress event time
* @return true if powerPress was consumed, false otherwise
*/
- public boolean onSinglePressDetected(long eventTime) {
+ public boolean shouldConsumeSinglePress(long eventTime) {
if (!mSideFpsEventHandlerReady.get()) {
return false;
}
switch (mBiometricState) {
case STATE_ENROLLING:
+ mHandler.post(
+ () -> {
+ if (mHandler.hasCallbacks(mTurnOffDialog)) {
+ Log.v(TAG, "Detected a tap to turn off dialog, ignoring");
+ mHandler.removeCallbacks(mTurnOffDialog);
+ }
+ });
+ showDialog(eventTime, "Enroll Power Press");
+ return true;
case STATE_BP_AUTH:
- mHandler.post(() -> {
- if (mDialog != null) {
- mDialog.dismiss();
- }
- mDialog = showConfirmDialog(mDialogSupplier.get(),
- mPowerManager, eventTime, mBiometricState, mDialogDismissListener);
- });
return true;
+ case STATE_KEYGUARD_AUTH:
default:
return false;
}
}
@NonNull
- private static Dialog showConfirmDialog(@NonNull AlertDialog.Builder dialogBuilder,
- @NonNull PowerManager powerManager, long eventTime,
+ private static Dialog showConfirmDialog(
+ @NonNull AlertDialog.Builder dialogBuilder,
+ @NonNull PowerManager powerManager,
+ long eventTime,
@BiometricStateListener.State int biometricState,
@NonNull DialogInterface.OnDismissListener dismissListener) {
final boolean enrolling = biometricState == STATE_ENROLLING;
- final int title = enrolling ? R.string.fp_power_button_enrollment_title
- : R.string.fp_power_button_bp_title;
- final int message = enrolling ? R.string.fp_power_button_enrollment_message
- : R.string.fp_power_button_bp_message;
- final int positiveText = enrolling ? R.string.fp_power_button_enrollment_positive_button
- : R.string.fp_power_button_bp_positive_button;
- final int negativeText = enrolling ? R.string.fp_power_button_enrollment_negative_button
- : R.string.fp_power_button_bp_negative_button;
-
- final Dialog confirmScreenOffDialog = dialogBuilder
- .setTitle(title)
- .setMessage(message)
- .setPositiveButton(positiveText,
- (dialog, which) -> {
- dialog.dismiss();
- powerManager.goToSleep(
- eventTime,
- PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
- 0 /* flags */
- );
- })
- .setNegativeButton(negativeText, (dialog, which) -> dialog.dismiss())
- .setOnDismissListener(dismissListener)
- .setCancelable(false)
- .create();
- confirmScreenOffDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
+ final int title =
+ enrolling
+ ? R.string.fp_power_button_enrollment_title
+ : R.string.fp_power_button_bp_title;
+ final int message =
+ enrolling
+ ? R.string.fp_power_button_enrollment_message
+ : R.string.fp_power_button_bp_message;
+ final int positiveText =
+ enrolling
+ ? R.string.fp_power_button_enrollment_positive_button
+ : R.string.fp_power_button_bp_positive_button;
+ final int negativeText =
+ enrolling
+ ? R.string.fp_power_button_enrollment_negative_button
+ : R.string.fp_power_button_bp_negative_button;
+
+ final Dialog confirmScreenOffDialog =
+ dialogBuilder
+ .setTitle(title)
+ .setMessage(message)
+ .setPositiveButton(
+ positiveText,
+ (dialog, which) -> {
+ dialog.dismiss();
+ powerManager.goToSleep(
+ eventTime,
+ PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
+ 0 /* flags */);
+ })
+ .setNegativeButton(negativeText, (dialog, which) -> dialog.dismiss())
+ .setOnDismissListener(dismissListener)
+ .setCancelable(false)
+ .create();
+ confirmScreenOffDialog
+ .getWindow()
+ .setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
confirmScreenOffDialog.show();
return confirmScreenOffDialog;
}
/**
- * Awaits notification from PhoneWindowManager that fingerprint service is ready
- * to send updates about power button fps sensor state. Then configures a
- * BiometricStateListener to receive and record updates to fps state, and
- * registers the BiometricStateListener in FingerprintManager.
+ * Awaits notification from PhoneWindowManager that fingerprint service is ready to send updates
+ * about power button fps sensor state. Then configures a BiometricStateListener to receive and
+ * record updates to fps state, and registers the BiometricStateListener in FingerprintManager.
*/
public void onFingerprintSensorReady() {
final PackageManager pm = mContext.getPackageManager();
@@ -184,12 +247,12 @@ public class SideFpsEventHandler {
if (fingerprintManager.isPowerbuttonFps()) {
fingerprintManager.registerBiometricStateListener(
new BiometricStateListener() {
- @Nullable
- private Runnable mStateRunnable = null;
+ @Nullable private Runnable mStateRunnable = null;
@Override
public void onStateChanged(
@BiometricStateListener.State int newState) {
+ Log.d(TAG, "onStateChanged : " + newState);
if (mStateRunnable != null) {
mHandler.removeCallbacks(mStateRunnable);
mStateRunnable = null;
@@ -200,16 +263,58 @@ public class SideFpsEventHandler {
// damper when moving to idle in case auth is first
if (newState == STATE_IDLE) {
mStateRunnable = () -> mBiometricState = newState;
- mHandler.postDelayed(mStateRunnable,
- DEBOUNCE_DELAY_MILLIS);
+ // This is also useful in the case of biometric
+ // prompt.
+ // If a user has recently succeeded/failed auth, we
+ // want to disable the power button for a short
+ // period of time (so ethey are able to view the
+ // prompt)
+ mHandler.postDelayed(
+ mStateRunnable, DEBOUNCE_DELAY_MILLIS);
+ dismissDialog("STATE_IDLE");
} else {
mBiometricState = newState;
}
}
+
+ @Override
+ public void onBiometricAction(
+ @BiometricStateListener.Action int action) {
+ Log.d(TAG, "onBiometricAction " + action);
+ switch (action) {
+ case BiometricStateListener.ACTION_SENSOR_TOUCH:
+ mHandler.postDelayed(
+ mTurnOffDialog,
+ mTapWaitForPowerDuration);
+ break;
+ }
+ }
});
mSideFpsEventHandlerReady.set(true);
}
}
});
}
+
+ private void dismissDialog(String reason) {
+ Log.d(TAG, "Dismissing dialog with reason: " + reason);
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
+ }
+ }
+
+ private void showDialog(long time, String reason) {
+ Log.d(TAG, "Showing dialog with reason: " + reason);
+ if (mDialog != null && mDialog.isShowing()) {
+ Log.d(TAG, "Ignoring show dialog");
+ return;
+ }
+ mDialog =
+ showConfirmDialog(
+ mDialogSupplier.get(),
+ mPowerManager,
+ time,
+ mBiometricState,
+ mDialogDismissListener);
+ }
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 50d1bd6c3e33..dbf05f1cd7c7 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -4801,6 +4801,11 @@ public final class PowerManagerService extends SystemService
.IS_STAY_ON_WHILE_PLUGGED_IN_WIRELESS,
((mStayOnWhilePluggedInSetting & BatteryManager.BATTERY_PLUGGED_WIRELESS)
!= 0));
+ proto.write(
+ PowerServiceSettingsAndConfigurationDumpProto.StayOnWhilePluggedInProto
+ .IS_STAY_ON_WHILE_PLUGGED_IN_DOCK,
+ ((mStayOnWhilePluggedInSetting & BatteryManager.BATTERY_PLUGGED_DOCK)
+ != 0));
proto.end(stayOnWhilePluggedInToken);
proto.write(
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 71b1bc2e24bc..bec3754536e2 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -1207,7 +1207,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
private int mImeBackDisposition = 0;
private boolean mShowImeSwitcher = false;
private IBinder mImeToken = null;
- private LetterboxDetails[] mLetterboxDetails;
+ private LetterboxDetails[] mLetterboxDetails = new LetterboxDetails[0];
private void setBarAttributes(@Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index f438e469577e..14898917caae 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4252,6 +4252,17 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
void detach(Transaction t) {
removeImeSurface(t);
}
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(64);
+ sb.append("ImeScreenshot{");
+ sb.append(Integer.toHexString(System.identityHashCode(this)));
+ sb.append(" imeTarget=" + mImeTarget);
+ sb.append(" surface=" + mImeSurface);
+ sb.append('}');
+ return sb.toString();
+ }
}
private void attachAndShowImeScreenshotOnTarget() {
@@ -4284,15 +4295,23 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
/**
- * Removes the IME screenshot when necessary.
- *
- * Used when app transition animation finished or obsoleted screenshot surface like size
- * changed by rotation.
+ * Removes the IME screenshot when the caller is a part of the attached target window.
*/
- void removeImeScreenshotIfPossible() {
- if (mImeLayeringTarget == null
- || mImeLayeringTarget.mAttrs.type != TYPE_APPLICATION_STARTING
- && !mImeLayeringTarget.inTransitionSelfOrParent()) {
+ void removeImeSurfaceByTarget(WindowContainer win) {
+ if (mImeScreenshot == null || win == null) {
+ return;
+ }
+ // The starting window shouldn't be the input target to attach the IME screenshot during
+ // transitioning.
+ if (win.asWindowState() != null
+ && win.asWindowState().mAttrs.type == TYPE_APPLICATION_STARTING) {
+ return;
+ }
+
+ final WindowState screenshotTarget = mImeScreenshot.getImeTarget();
+ final boolean winIsOrContainsScreenshotTarget = (win == screenshotTarget
+ || win.getWindow(w -> w == screenshotTarget) != null);
+ if (winIsOrContainsScreenshotTarget) {
removeImeSurfaceImmediately();
}
}
@@ -4640,10 +4659,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
wc, SurfaceAnimator.animationTypeToString(type), mImeScreenshot,
mImeScreenshot.getImeTarget());
}
- if (mImeScreenshot != null && (wc == mImeScreenshot.getImeTarget()
- || wc.getWindow(w -> w == mImeScreenshot.getImeTarget()) != null)
- && (type & WindowState.EXIT_ANIMATING_TYPES) != 0) {
- removeImeSurfaceImmediately();
+ if ((type & WindowState.EXIT_ANIMATING_TYPES) != 0) {
+ removeImeSurfaceByTarget(wc);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index af8c4c8e9370..fa78a2b4aad8 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2449,8 +2449,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final DisplayContent dc = getDisplayContent();
if (isImeLayeringTarget()) {
- // Remove the IME screenshot surface if the layering target is not animating.
- dc.removeImeScreenshotIfPossible();
+ // Remove the attached IME screenshot surface.
+ dc.removeImeSurfaceByTarget(this);
// Make sure to set mImeLayeringTarget as null when the removed window is the
// IME target, in case computeImeTarget may use the outdated target.
dc.setImeLayeringTarget(null);
@@ -3584,6 +3584,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
} else {
logExclusionRestrictions(EXCLUSION_LEFT);
logExclusionRestrictions(EXCLUSION_RIGHT);
+ getDisplayContent().removeImeSurfaceByTarget(this);
}
// Exclude toast because legacy apps may show toast window by themselves, so the misused
// apps won't always be considered as foreground state.
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index 1a49f8aa2e6a..ea1e49d8748a 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -43,8 +43,10 @@ import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import android.testing.TestableContext;
@@ -128,6 +130,8 @@ public class FingerprintAuthenticationClientTest {
@Captor
private ArgumentCaptor<Consumer<OperationContext>> mContextInjector;
+ private TestLooper mLooper = new TestLooper();
+
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
@@ -233,6 +237,9 @@ public class FingerprintAuthenticationClientTest {
client.start(mCallback);
client.onAuthenticated(new Fingerprint("name", 2 /* enrollmentId */, SENSOR_ID),
true /* authenticated */, new ArrayList<>());
+
+ mLooper.moveTimeForward(10);
+ mLooper.dispatchAll();
verify(mLuxProbe).destroy();
client.onAcquired(2, 0);
@@ -309,9 +316,58 @@ public class FingerprintAuthenticationClientTest {
client.onAuthenticated(new Fingerprint("friendly", 1 /* fingerId */, 2 /* deviceId */),
true /* authenticated */, new ArrayList<>());
+ mLooper.moveTimeForward(10);
+ mLooper.dispatchAll();
verify(mCancellationSignal).cancel();
}
+ @Test
+ public void fingerprintPowerIgnoresAuthInWindow() throws Exception {
+ when(mSensorProps.isAnySidefpsType()).thenReturn(true);
+
+ final FingerprintAuthenticationClient client = createClient(1);
+ client.start(mCallback);
+ client.onPowerPressed();
+ client.onAuthenticated(new Fingerprint("friendly", 1 /* fingerId */, 2 /* deviceId */),
+ true /* authenticated */, new ArrayList<>());
+ mLooper.moveTimeForward(1000);
+ mLooper.dispatchAll();
+
+ verify(mCallback).onClientFinished(any(), eq(false));
+ }
+
+ @Test
+ public void fingerprintAuthIgnoredWaitingForPower() throws Exception {
+ when(mSensorProps.isAnySidefpsType()).thenReturn(true);
+
+ final FingerprintAuthenticationClient client = createClient(1);
+ client.start(mCallback);
+ client.onAuthenticated(new Fingerprint("friendly", 3 /* fingerId */, 4 /* deviceId */),
+ true /* authenticated */, new ArrayList<>());
+ client.onPowerPressed();
+ mLooper.moveTimeForward(1000);
+ mLooper.dispatchAll();
+
+ verify(mCallback).onClientFinished(any(), eq(false));
+ }
+
+ @Test
+ public void fingerprintAuthSucceedsAfterPowerWindow() throws Exception {
+ when(mSensorProps.isAnySidefpsType()).thenReturn(true);
+
+ final FingerprintAuthenticationClient client = createClient(1);
+ client.start(mCallback);
+ client.onPowerPressed();
+ mLooper.moveTimeForward(1000);
+ mLooper.dispatchAll();
+ client.onAuthenticated(new Fingerprint("friendly", 4 /* fingerId */, 5 /* deviceId */),
+ true /* authenticated */, new ArrayList<>());
+ mLooper.moveTimeForward(1000);
+ mLooper.dispatchAll();
+
+ verify(mCallback).onClientFinished(any(), eq(true));
+ }
+
private FingerprintAuthenticationClient createClient() throws RemoteException {
return createClient(100 /* version */, true /* allowBackgroundAuthentication */);
}
@@ -336,7 +392,8 @@ public class FingerprintAuthenticationClientTest {
9 /* sensorId */, mBiometricLogger, mBiometricContext,
true /* isStrongBiometric */,
null /* taskStackListener */, mLockoutCache,
- mUdfpsOverlayController, mSideFpsController, allowBackgroundAuthentication, mSensorProps) {
+ mUdfpsOverlayController, mSideFpsController, allowBackgroundAuthentication, mSensorProps,
+ new Handler(mLooper.getLooper())) {
@Override
protected ActivityTaskManager getActivityTaskManager() {
return mActivityTaskManager;
diff --git a/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java b/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java
index 371861f74871..7746bd6859d6 100644
--- a/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java
@@ -53,33 +53,30 @@ import java.util.List;
/**
* Unit tests for {@link SideFpsEventHandler}.
- * <p/>
- * Run with <code>atest SideFpsEventHandlerTest</code>.
+ *
+ * <p>Run with <code>atest SideFpsEventHandlerTest</code>.
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class SideFpsEventHandlerTest {
- private static final List<Integer> sAllStates = List.of(
- BiometricStateListener.STATE_IDLE,
- BiometricStateListener.STATE_ENROLLING,
- BiometricStateListener.STATE_KEYGUARD_AUTH,
- BiometricStateListener.STATE_BP_AUTH,
- BiometricStateListener.STATE_AUTH_OTHER);
+ private static final List<Integer> sAllStates =
+ List.of(
+ BiometricStateListener.STATE_IDLE,
+ BiometricStateListener.STATE_ENROLLING,
+ BiometricStateListener.STATE_KEYGUARD_AUTH,
+ BiometricStateListener.STATE_BP_AUTH,
+ BiometricStateListener.STATE_AUTH_OTHER);
@Rule
public TestableContext mContext =
new TestableContext(InstrumentationRegistry.getContext(), null);
- @Mock
- private PackageManager mPackageManager;
- @Mock
- private FingerprintManager mFingerprintManager;
- @Spy
- private AlertDialog.Builder mDialogBuilder = new AlertDialog.Builder(mContext);
- @Mock
- private AlertDialog mAlertDialog;
- @Mock
- private Window mWindow;
+
+ @Mock private PackageManager mPackageManager;
+ @Mock private FingerprintManager mFingerprintManager;
+ @Spy private AlertDialog.Builder mDialogBuilder = new AlertDialog.Builder(mContext);
+ @Mock private AlertDialog mAlertDialog;
+ @Mock private Window mWindow;
private TestLooper mLooper = new TestLooper();
private SideFpsEventHandler mEventHandler;
@@ -95,9 +92,12 @@ public class SideFpsEventHandlerTest {
when(mDialogBuilder.create()).thenReturn(mAlertDialog);
when(mAlertDialog.getWindow()).thenReturn(mWindow);
- mEventHandler = new SideFpsEventHandler(
- mContext, new Handler(mLooper.getLooper()),
- mContext.getSystemService(PowerManager.class), () -> mDialogBuilder);
+ mEventHandler =
+ new SideFpsEventHandler(
+ mContext,
+ new Handler(mLooper.getLooper()),
+ mContext.getSystemService(PowerManager.class),
+ () -> mDialogBuilder);
}
@Test
@@ -105,7 +105,7 @@ public class SideFpsEventHandlerTest {
when(mPackageManager.hasSystemFeature(eq(PackageManager.FEATURE_FINGERPRINT)))
.thenReturn(false);
- assertThat(mEventHandler.onSinglePressDetected(60L)).isFalse();
+ assertThat(mEventHandler.shouldConsumeSinglePress(60L)).isFalse();
mLooper.dispatchAll();
verify(mAlertDialog, never()).show();
@@ -117,7 +117,7 @@ public class SideFpsEventHandlerTest {
for (int state : sAllStates) {
setBiometricState(state);
- assertThat(mEventHandler.onSinglePressDetected(200L)).isFalse();
+ assertThat(mEventHandler.shouldConsumeSinglePress(200L)).isFalse();
mLooper.dispatchAll();
verify(mAlertDialog, never()).show();
@@ -130,7 +130,7 @@ public class SideFpsEventHandlerTest {
for (int state : sAllStates) {
setBiometricState(state);
- assertThat(mEventHandler.onSinglePressDetected(400L)).isFalse();
+ assertThat(mEventHandler.shouldConsumeSinglePress(400L)).isFalse();
mLooper.dispatchAll();
verify(mAlertDialog, never()).show();
@@ -139,13 +139,13 @@ public class SideFpsEventHandlerTest {
@Test
public void ignoresWhenIdleOrUnknown() throws Exception {
- setupWithSensor(true /* hasSfps */, true /* initialized */);
+ setupWithSensor(true /* hasSidefps */, true /* initialized */);
setBiometricState(BiometricStateListener.STATE_IDLE);
- assertThat(mEventHandler.onSinglePressDetected(80000L)).isFalse();
+ assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isFalse();
setBiometricState(BiometricStateListener.STATE_AUTH_OTHER);
- assertThat(mEventHandler.onSinglePressDetected(90000L)).isFalse();
+ assertThat(mEventHandler.shouldConsumeSinglePress(90000L)).isFalse();
mLooper.dispatchAll();
verify(mAlertDialog, never()).show();
@@ -156,7 +156,7 @@ public class SideFpsEventHandlerTest {
setupWithSensor(true /* hasSfps */, true /* initialized */);
setBiometricState(BiometricStateListener.STATE_KEYGUARD_AUTH);
- assertThat(mEventHandler.onSinglePressDetected(80000L)).isFalse();
+ assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isFalse();
mLooper.dispatchAll();
verify(mAlertDialog, never()).show();
@@ -164,13 +164,13 @@ public class SideFpsEventHandlerTest {
@Test
public void promptsWhenBPisActive() throws Exception {
- setupWithSensor(true /* hasSfps */, true /* initialized */);
+ setupWithSensor(true /* hasSideFps */, true /* initialized */);
setBiometricState(BiometricStateListener.STATE_BP_AUTH);
- assertThat(mEventHandler.onSinglePressDetected(80000L)).isTrue();
+ assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isTrue();
mLooper.dispatchAll();
- verify(mAlertDialog).show();
+ verify(mAlertDialog, never()).show();
}
@Test
@@ -178,7 +178,57 @@ public class SideFpsEventHandlerTest {
setupWithSensor(true /* hasSfps */, true /* initialized */);
setBiometricState(BiometricStateListener.STATE_ENROLLING);
- assertThat(mEventHandler.onSinglePressDetected(80000L)).isTrue();
+ assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isTrue();
+
+ mLooper.dispatchAll();
+ verify(mAlertDialog).show();
+ verify(mAlertDialog, never()).dismiss();
+ }
+
+ @Test
+ public void dismissesDialogOnTouchWhenEnrolling() throws Exception {
+ setupWithSensor(true /* hasSfps */, true /* initialized */);
+
+ setBiometricState(BiometricStateListener.STATE_ENROLLING);
+ when(mAlertDialog.isShowing()).thenReturn(true);
+ assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isTrue();
+
+ mLooper.dispatchAll();
+ verify(mAlertDialog).show();
+
+ mBiometricStateListener.onBiometricAction(BiometricStateListener.ACTION_SENSOR_TOUCH);
+ mLooper.moveTimeForward(10000);
+ mLooper.dispatchAll();
+
+ verify(mAlertDialog).dismiss();
+ }
+
+ @Test
+ public void dismissesDialogFailsWhenPowerPressedAndDialogShowing() throws Exception {
+ setupWithSensor(true /* hasSfps */, true /* initialized */);
+
+ setBiometricState(BiometricStateListener.STATE_ENROLLING);
+ when(mAlertDialog.isShowing()).thenReturn(true);
+ assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isTrue();
+
+ mLooper.dispatchAll();
+ verify(mAlertDialog).show();
+
+ mBiometricStateListener.onBiometricAction(BiometricStateListener.ACTION_SENSOR_TOUCH);
+ assertThat(mEventHandler.shouldConsumeSinglePress(60L)).isTrue();
+
+ mLooper.dispatchAll();
+ verify(mAlertDialog, never()).dismiss();
+ }
+
+ @Test
+ public void showDialogAfterTap() throws Exception {
+ setupWithSensor(true /* hasSfps */, true /* initialized */);
+
+ setBiometricState(BiometricStateListener.STATE_ENROLLING);
+ when(mAlertDialog.isShowing()).thenReturn(true);
+ mBiometricStateListener.onBiometricAction(BiometricStateListener.ACTION_SENSOR_TOUCH);
+ assertThat(mEventHandler.shouldConsumeSinglePress(60L)).isTrue();
mLooper.dispatchAll();
verify(mAlertDialog).show();
@@ -201,11 +251,13 @@ public class SideFpsEventHandlerTest {
ArgumentCaptor.forClass(IFingerprintAuthenticatorsRegisteredCallback.class);
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(fpCallbackCaptor.capture());
if (initialized) {
- fpCallbackCaptor.getValue().onAllAuthenticatorsRegistered(
- List.of(mock(FingerprintSensorPropertiesInternal.class)));
+ fpCallbackCaptor
+ .getValue()
+ .onAllAuthenticatorsRegistered(
+ List.of(mock(FingerprintSensorPropertiesInternal.class)));
if (hasSfps) {
- ArgumentCaptor<BiometricStateListener> captor = ArgumentCaptor.forClass(
- BiometricStateListener.class);
+ ArgumentCaptor<BiometricStateListener> captor =
+ ArgumentCaptor.forClass(BiometricStateListener.class);
verify(mFingerprintManager).registerBiometricStateListener(captor.capture());
mBiometricStateListener = captor.getValue();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 1f07b20acc13..d6b807fc223e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -2207,6 +2207,52 @@ public class DisplayContentTests extends WindowTestsBase {
assertNotEquals(curSnapshot, mDisplayContent.mImeScreenshot);
}
+ @UseTestDisplay(addWindows = {W_INPUT_METHOD})
+ @Test
+ public void testRemoveImeScreenshot_whenTargetSurfaceWasInvisible() {
+ final Task rootTask = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+ final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
+ final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
+ win.onSurfaceShownChanged(true);
+ makeWindowVisible(win, mDisplayContent.mInputMethodWindow);
+ task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE);
+ doReturn(true).when(task).okToAnimate();
+ ArrayList<WindowContainer> sources = new ArrayList<>();
+ sources.add(activity);
+
+ mDisplayContent.setImeLayeringTarget(win);
+ mDisplayContent.setImeInputTarget(win);
+ mDisplayContent.getInsetsStateController().getImeSourceProvider().setImeShowing(true);
+ task.applyAnimation(null, TRANSIT_OLD_TASK_CLOSE, false /* enter */,
+ false /* isVoiceInteraction */, sources);
+ assertNotNull(mDisplayContent.mImeScreenshot);
+
+ win.onSurfaceShownChanged(false);
+ assertNull(mDisplayContent.mImeScreenshot);
+ }
+
+ @UseTestDisplay(addWindows = {W_INPUT_METHOD})
+ @Test
+ public void testRemoveImeScreenshot_whenWindowRemoveImmediately() {
+ final Task rootTask = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+ final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
+ final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
+ makeWindowVisible(mDisplayContent.mInputMethodWindow);
+
+ mDisplayContent.setImeLayeringTarget(win);
+ mDisplayContent.setImeInputTarget(win);
+ mDisplayContent.getInsetsStateController().getImeSourceProvider().setImeShowing(true);
+ mDisplayContent.showImeScreenshot();
+ assertNotNull(mDisplayContent.mImeScreenshot);
+
+ // Expect IME snapshot will be removed when the win is IME layering target and invoked
+ // removeImeSurfaceByTarget.
+ win.removeImmediately();
+ assertNull(mDisplayContent.mImeScreenshot);
+ }
+
@Test
public void testRotateBounds_keepSamePhysicalPosition() {
final DisplayContent dc =