Merge "Add Gainmap bitmap & imagedecoder"
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index a976de3..2ab8ac0 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -58,6 +58,19 @@
+ "180℃で揚げ、衣がきつね色になったら引き上げて、油を切る。"
+ "盛り付けて出来上がり。";
+ public static final String KO_TEXT_SHORT = "모든 인류 구성원의 천부의 존엄성과 동등하고 양도할 수 없는 권리를";
+ // About 530 chars
+ public static final String KO_TEXT_LONG = "모든 인류 구성원의 천부의 존엄성과 동등하고 양도할 수 없는 권리를"
+ + " 인정하는 것이 세계의 자유, 정의 및 평화의 기초이며, 인권에 대한 무시와 경멸이 인류의 양심을 격분시키는"
+ + " 만행을 초래하였으며, 인간이 언론과 신앙의 자유, 그리고 공포와 결핍으로부터의 자유를 누릴 수 있는 세계의"
+ + " 도래가 모든 사람들의 지고한 열망으로서 천명되어 왔으며, 인간이 폭정과 억압에 대항하는 마지막 수단으로서"
+ + " 반란을 일으키도록 강요받지 않으려면, 법에 의한 통치에 의하여 인권이 보호되어야 하는 것이 필수적이며,"
+ + "국가간에 우호관계의 발전을 증진하는 것이 필수적이며, 국제연합의 모든 사람들은 그 헌장에서 기본적 인권,"
+ + " 인간의 존엄과 가치, 그리고 남녀의 동등한 권리에 대한 신념을 재확인하였으며, 보다 폭넓은 "
+ + "자유속에서 사회적 진보와 보다 나은 생활수준을 증진하기로 다짐하였고, 회원국들은 국제연합과 협력하여 인권과"
+ + " 기본적 자유의 보편적 존중과 준수를 증진할 것을 스스로 서약하였으며, 이러한 권리와 자유에 대한 공통의"
+ + " 이해가 이 서약의 완전한 이행을 위하여 가장 중요하므로,";
+
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@@ -560,4 +573,118 @@
.build();
}
}
+
+ @Test
+ public void testCreate_KOText_Phrase_Short() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final String text = KO_TEXT_SHORT;
+ final LineBreakConfig config = new LineBreakConfig.Builder()
+ .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
+ .build();
+ final TextPaint paint = new TextPaint(PAINT);
+ paint.setTextLocales(LocaleList.forLanguageTags("ko-KR"));
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+ StaticLayout.Builder.obtain(text, 0, text.length(), paint, TEXT_WIDTH)
+ .setLineBreakConfig(config)
+ .build();
+ }
+ }
+
+ @Test
+ public void testCreate_KOText_Phrase_Long() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final String text = KO_TEXT_LONG;
+ final LineBreakConfig config = new LineBreakConfig.Builder()
+ .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
+ .build();
+ final TextPaint paint = new TextPaint(PAINT);
+ paint.setTextLocales(LocaleList.forLanguageTags("ko-KR"));
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+ StaticLayout.Builder.obtain(text, 0, text.length(), paint, TEXT_WIDTH)
+ .setLineBreakConfig(config)
+ .build();
+ }
+ }
+
+ @Test
+ public void testCreate_KOText_Phrase_LongLong() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final String text = KO_TEXT_LONG.repeat(20); // 250 * 20 = 7000 chars
+ final LineBreakConfig config = new LineBreakConfig.Builder()
+ .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
+ .build();
+ final TextPaint paint = new TextPaint(PAINT);
+ paint.setTextLocales(LocaleList.forLanguageTags("ko-KR"));
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+ StaticLayout.Builder.obtain(text, 0, text.length(), paint, TEXT_WIDTH)
+ .setLineBreakConfig(config)
+ .build();
+ }
+ }
+
+ @Test
+ public void testCreate_KOText_NoPhrase_Short() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final String text = KO_TEXT_SHORT;
+ final LineBreakConfig config = new LineBreakConfig.Builder()
+ .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE)
+ .build();
+ final TextPaint paint = new TextPaint(PAINT);
+ paint.setTextLocales(LocaleList.forLanguageTags("ko-KR"));
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+ StaticLayout.Builder.obtain(text, 0, text.length(), paint, TEXT_WIDTH)
+ .setLineBreakConfig(config)
+ .build();
+ }
+ }
+
+ @Test
+ public void testCreate_KOText_NoPhrase_Long() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final String text = KO_TEXT_LONG;
+ final LineBreakConfig config = new LineBreakConfig.Builder()
+ .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE)
+ .build();
+ final TextPaint paint = new TextPaint(PAINT);
+ paint.setTextLocales(LocaleList.forLanguageTags("ko-KR"));
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+ StaticLayout.Builder.obtain(text, 0, text.length(), paint, TEXT_WIDTH)
+ .setLineBreakConfig(config)
+ .build();
+ }
+ }
+
+ @Test
+ public void testCreate_KOText_NoPhrase_LongLong() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final String text = KO_TEXT_LONG.repeat(20); // 520 * 20 = 10400 chars
+ final LineBreakConfig config = new LineBreakConfig.Builder()
+ .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE)
+ .build();
+ final TextPaint paint = new TextPaint(PAINT);
+ paint.setTextLocales(LocaleList.forLanguageTags("ko-KR"));
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+ StaticLayout.Builder.obtain(text, 0, text.length(), paint, TEXT_WIDTH)
+ .setLineBreakConfig(config)
+ .build();
+ }
+ }
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 978713a..2b1616d 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -64,6 +64,8 @@
field public static final String BLUETOOTH_SCAN = "android.permission.BLUETOOTH_SCAN";
field public static final String BODY_SENSORS = "android.permission.BODY_SENSORS";
field public static final String BODY_SENSORS_BACKGROUND = "android.permission.BODY_SENSORS_BACKGROUND";
+ field public static final String BODY_SENSORS_WRIST_TEMPERATURE = "android.permission.BODY_SENSORS_WRIST_TEMPERATURE";
+ field public static final String BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND = "android.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND";
field public static final String BROADCAST_PACKAGE_REMOVED = "android.permission.BROADCAST_PACKAGE_REMOVED";
field public static final String BROADCAST_SMS = "android.permission.BROADCAST_SMS";
field public static final String BROADCAST_STICKY = "android.permission.BROADCAST_STICKY";
@@ -4845,6 +4847,7 @@
field public static final String OPSTR_ADD_VOICEMAIL = "android:add_voicemail";
field public static final String OPSTR_ANSWER_PHONE_CALLS = "android:answer_phone_calls";
field public static final String OPSTR_BODY_SENSORS = "android:body_sensors";
+ field public static final String OPSTR_BODY_SENSORS_WRIST_TEMPERATURE = "android:body_sensors_wrist_temperature";
field public static final String OPSTR_CALL_PHONE = "android:call_phone";
field public static final String OPSTR_CAMERA = "android:camera";
field public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -6736,6 +6739,7 @@
method public static void writePendingIntentOrNullToParcel(@Nullable android.app.PendingIntent, @NonNull android.os.Parcel);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.PendingIntent> CREATOR;
+ field public static final int FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT = 16777216; // 0x1000000
field public static final int FLAG_CANCEL_CURRENT = 268435456; // 0x10000000
field public static final int FLAG_IMMUTABLE = 67108864; // 0x4000000
field public static final int FLAG_MUTABLE = 33554432; // 0x2000000
@@ -12738,7 +12742,7 @@
field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_CAMERA}, anyOf={android.Manifest.permission.CAMERA}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_CAMERA = 64; // 0x40
field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE}, anyOf={android.Manifest.permission.BLUETOOTH_ADVERTISE, android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.CHANGE_NETWORK_STATE, android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE, android.Manifest.permission.NFC, android.Manifest.permission.TRANSMIT_IR, android.Manifest.permission.UWB_RANGING}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE = 16; // 0x10
field @RequiresPermission(value=android.Manifest.permission.FOREGROUND_SERVICE_DATA_SYNC, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_DATA_SYNC = 1; // 0x1
- field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_HEALTH}, anyOf={android.Manifest.permission.ACTIVITY_RECOGNITION, android.Manifest.permission.BODY_SENSORS, android.Manifest.permission.HIGH_SAMPLING_RATE_SENSORS}) public static final int FOREGROUND_SERVICE_TYPE_HEALTH = 256; // 0x100
+ field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_HEALTH}, anyOf={android.Manifest.permission.ACTIVITY_RECOGNITION, android.Manifest.permission.BODY_SENSORS, android.Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE, android.Manifest.permission.HIGH_SAMPLING_RATE_SENSORS}) public static final int FOREGROUND_SERVICE_TYPE_HEALTH = 256; // 0x100
field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_LOCATION}, anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 8; // 0x8
field public static final int FOREGROUND_SERVICE_TYPE_MANIFEST = -1; // 0xffffffff
field @RequiresPermission(value=android.Manifest.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK = 2; // 0x2
@@ -13343,10 +13347,22 @@
field @NonNull public static final String TYPE_PASSWORD_CREDENTIAL = "android.credentials.TYPE_PASSWORD_CREDENTIAL";
}
+ public final class CredentialDescription implements android.os.Parcelable {
+ ctor public CredentialDescription(@NonNull String, @NonNull String, @NonNull java.util.List<android.service.credentials.CredentialEntry>);
+ method public int describeContents();
+ method @NonNull public java.util.List<android.service.credentials.CredentialEntry> getCredentialEntries();
+ method @NonNull public String getFlattenedRequestString();
+ method @NonNull public String getType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.credentials.CredentialDescription> CREATOR;
+ }
+
public final class CredentialManager {
method public void clearCredentialState(@NonNull android.credentials.ClearCredentialStateRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.credentials.ClearCredentialStateException>);
method public void createCredential(@NonNull android.credentials.CreateCredentialRequest, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.CreateCredentialResponse,android.credentials.CreateCredentialException>);
method public void getCredential(@NonNull android.credentials.GetCredentialRequest, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>);
+ method public void registerCredentialDescription(@NonNull android.credentials.RegisterCredentialDescriptionRequest);
+ method public void unregisterCredentialDescription(@NonNull android.credentials.UnregisterCredentialDescriptionRequest);
}
public class GetCredentialException extends java.lang.Exception {
@@ -13370,6 +13386,7 @@
method public boolean isSystemProviderRequired();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.credentials.GetCredentialOption> CREATOR;
+ field public static final String FLATTENED_REQUEST = "android.credentials.GetCredentialOption.FLATTENED_REQUEST_STRING";
}
public final class GetCredentialRequest implements android.os.Parcelable {
@@ -13395,6 +13412,24 @@
field @NonNull public static final android.os.Parcelable.Creator<android.credentials.GetCredentialResponse> CREATOR;
}
+ public final class RegisterCredentialDescriptionRequest implements android.os.Parcelable {
+ ctor public RegisterCredentialDescriptionRequest(@NonNull android.credentials.CredentialDescription);
+ ctor public RegisterCredentialDescriptionRequest(@NonNull java.util.Set<android.credentials.CredentialDescription>);
+ method public int describeContents();
+ method @NonNull public java.util.Set<android.credentials.CredentialDescription> getCredentialDescriptions();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.credentials.RegisterCredentialDescriptionRequest> CREATOR;
+ }
+
+ public final class UnregisterCredentialDescriptionRequest implements android.os.Parcelable {
+ ctor public UnregisterCredentialDescriptionRequest(@NonNull android.credentials.CredentialDescription);
+ ctor public UnregisterCredentialDescriptionRequest(@NonNull java.util.List<android.credentials.CredentialDescription>);
+ method public int describeContents();
+ method @NonNull public java.util.List<android.credentials.CredentialDescription> getCredentialDescriptions();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.credentials.UnregisterCredentialDescriptionRequest> CREATOR;
+ }
+
}
package android.database {
@@ -48637,7 +48672,7 @@
field public float density;
field public int densityDpi;
field public int heightPixels;
- field public float scaledDensity;
+ field @Deprecated public float scaledDensity;
field public int widthPixels;
field public float xdpi;
field public float ydpi;
@@ -49551,6 +49586,7 @@
field public static final int HDR_TYPE_HDR10 = 2; // 0x2
field public static final int HDR_TYPE_HDR10_PLUS = 4; // 0x4
field public static final int HDR_TYPE_HLG = 3; // 0x3
+ field public static final int HDR_TYPE_INVALID = -1; // 0xffffffff
field public static final float INVALID_LUMINANCE = -1.0f;
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index b6232cd..5f2f623 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -30,6 +30,7 @@
field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS";
field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
field public static final String MANAGE_TOAST_RATE_LIMITING = "android.permission.MANAGE_TOAST_RATE_LIMITING";
+ field public static final String MODIFY_HDR_CONVERSION_MODE = "android.permission.MODIFY_HDR_CONVERSION_MODE";
field public static final String MODIFY_REFRESH_RATE_SWITCHING_TYPE = "android.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE";
field public static final String MODIFY_USER_PREFERRED_DISPLAY_MODE = "android.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE";
field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
@@ -132,6 +133,7 @@
method @RequiresPermission(allOf={android.Manifest.permission.PACKAGE_USAGE_STATS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional=true) public int getUidProcessState(int);
method public void holdLock(android.os.IBinder, int);
method public static boolean isHighEndGfx();
+ method public void notifySystemPropertiesChanged();
method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener);
method @RequiresPermission(android.Manifest.permission.RESET_APP_ERRORS) public void resetAppErrors();
method public static void resumeAppSwitches() throws android.os.RemoteException;
@@ -983,59 +985,6 @@
}
-package android.credentials {
-
- public final class CredentialDescription implements android.os.Parcelable {
- ctor public CredentialDescription(@NonNull String, @NonNull String, @NonNull java.util.List<android.service.credentials.CredentialEntry>);
- method public int describeContents();
- method @NonNull public java.util.List<android.service.credentials.CredentialEntry> getCredentialEntries();
- method @NonNull public String getFlattenedRequestString();
- method @NonNull public String getType();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.credentials.CredentialDescription> CREATOR;
- }
-
- public final class CredentialManager {
- method public void registerCredentialDescription(@NonNull android.credentials.RegisterCredentialDescriptionRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.credentials.RegisterCredentialDescriptionException>);
- method public void unRegisterCredentialDescription(@NonNull android.credentials.UnregisterCredentialDescriptionRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.credentials.UnregisterCredentialDescriptionException>);
- }
-
- public class RegisterCredentialDescriptionException extends java.lang.Exception {
- ctor public RegisterCredentialDescriptionException(@NonNull String, @Nullable String);
- ctor public RegisterCredentialDescriptionException(@NonNull String, @Nullable String, @Nullable Throwable);
- ctor public RegisterCredentialDescriptionException(@NonNull String, @Nullable Throwable);
- ctor public RegisterCredentialDescriptionException(@NonNull String);
- field @NonNull public final String errorType;
- }
-
- public final class RegisterCredentialDescriptionRequest implements android.os.Parcelable {
- ctor public RegisterCredentialDescriptionRequest(@NonNull android.credentials.CredentialDescription);
- ctor public RegisterCredentialDescriptionRequest(@NonNull java.util.List<android.credentials.CredentialDescription>);
- method public int describeContents();
- method @NonNull public java.util.List<android.credentials.CredentialDescription> getCredentialDescriptions();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.credentials.RegisterCredentialDescriptionRequest> CREATOR;
- field public static final String FLATTENED_REQUEST_STRING_KEY = "flattened_request_string";
- }
-
- public class UnregisterCredentialDescriptionException extends java.lang.Exception {
- ctor public UnregisterCredentialDescriptionException(@NonNull String, @Nullable String);
- ctor public UnregisterCredentialDescriptionException(@NonNull String, @Nullable String, @Nullable Throwable);
- ctor public UnregisterCredentialDescriptionException(@NonNull String, @Nullable Throwable);
- ctor public UnregisterCredentialDescriptionException(@NonNull String);
- field @NonNull public final String errorType;
- }
-
- public final class UnregisterCredentialDescriptionRequest implements android.os.Parcelable {
- ctor public UnregisterCredentialDescriptionRequest(@NonNull android.credentials.CredentialDescription);
- method public int describeContents();
- method @NonNull public android.credentials.CredentialDescription getCredentialDescription();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.credentials.UnregisterCredentialDescriptionRequest> CREATOR;
- }
-
-}
-
package android.credentials.ui {
public final class CreateCredentialProviderData extends android.credentials.ui.ProviderData implements android.os.Parcelable {
@@ -1392,11 +1341,14 @@
method public boolean areUserDisabledHdrTypesAllowed();
method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void clearGlobalUserPreferredDisplayMode();
method @Nullable public android.view.Display.Mode getGlobalUserPreferredDisplayMode();
+ method @NonNull public android.hardware.display.HdrConversionMode getHdrConversionMode();
+ method @NonNull public int[] getSupportedHdrOutputTypes();
method @NonNull public int[] getUserDisabledHdrTypes();
method public boolean isMinimalPostProcessingRequested(int);
method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public void overrideHdrTypes(int, @NonNull int[]);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setAreUserDisabledHdrTypesAllowed(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void setGlobalUserPreferredDisplayMode(@NonNull android.view.Display.Mode);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_HDR_CONVERSION_MODE) public void setHdrConversionMode(@NonNull android.hardware.display.HdrConversionMode);
method @RequiresPermission(android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) public void setRefreshRateSwitchingType(int);
method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public void setShouldAlwaysRespectAppRequestedMode(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setUserDisabledHdrTypes(@NonNull int[]);
@@ -1409,6 +1361,19 @@
field public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 512; // 0x200
}
+ public final class HdrConversionMode implements android.os.Parcelable {
+ ctor public HdrConversionMode(int, int);
+ ctor public HdrConversionMode(int);
+ method public int describeContents();
+ method public int getConversionMode();
+ method public int getPreferredHdrOutputType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.display.HdrConversionMode> CREATOR;
+ field public static final int HDR_CONVERSION_FORCE = 3; // 0x3
+ field public static final int HDR_CONVERSION_PASSTHROUGH = 1; // 0x1
+ field public static final int HDR_CONVERSION_SYSTEM = 2; // 0x2
+ }
+
}
package android.hardware.fingerprint {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index b626493..c4d6ad7 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -5346,6 +5346,20 @@
}
/**
+ * Checks if the process represented by the given {@code pid} is frozen.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.DUMP)
+ public boolean isProcessFrozen(int pid) {
+ try {
+ return getService().isProcessFrozen(pid);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* @return The reason code of whether or not the given UID should be exempted from background
* restrictions here.
*
@@ -5367,6 +5381,31 @@
}
/**
+ * Notifies {@link #getRunningAppProcesses app processes} that the system properties
+ * have changed.
+ *
+ * @see SystemProperties#addChangeCallback
+ *
+ * @hide
+ */
+ @TestApi
+ public void notifySystemPropertiesChanged() {
+ // Note: this cannot use {@link ServiceManager#listServices()} to notify all the services,
+ // as that is not available from tests.
+ final var binder = ActivityManager.getService().asBinder();
+ if (binder != null) {
+ var data = Parcel.obtain();
+ try {
+ binder.transact(IBinder.SYSPROPS_TRANSACTION, data, null /* reply */,
+ 0 /* flags */);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ data.recycle();
+ }
+ }
+
+ /**
* A subset of immutable pending intent information suitable for caching on the client side.
*
* @hide
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 84320ca..c89a769 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1448,9 +1448,13 @@
public static final int OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD =
AppProtoEnums.APP_OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD;
+ /** @hide Access to wrist temperature sensors. */
+ public static final int OP_BODY_SENSORS_WRIST_TEMPERATURE =
+ AppProtoEnums.APP_OP_BODY_SENSORS_WRIST_TEMPERATURE;
+
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int _NUM_OP = 132;
+ public static final int _NUM_OP = 133;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -2030,6 +2034,10 @@
public static final String OPSTR_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD =
"android:capture_consentless_bugreport_on_userdebug_build";
+ /** Access to wrist temperature body sensors. */
+ public static final String OPSTR_BODY_SENSORS_WRIST_TEMPERATURE =
+ "android:body_sensors_wrist_temperature";
+
/** {@link #sAppOpsToNote} not initialized yet for this op */
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
/** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -2128,6 +2136,7 @@
OP_READ_MEDIA_VISUAL_USER_SELECTED,
OP_FOREGROUND_SERVICE_SPECIAL_USE,
OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD,
+ OP_BODY_SENSORS_WRIST_TEMPERATURE,
};
static final AppOpInfo[] sAppOpInfos = new AppOpInfo[]{
@@ -2541,7 +2550,12 @@
OPSTR_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD,
"CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD")
.setPermission(Manifest.permission.CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD)
- .build()
+ .build(),
+ new AppOpInfo.Builder(OP_BODY_SENSORS_WRIST_TEMPERATURE,
+ OPSTR_BODY_SENSORS_WRIST_TEMPERATURE,
+ "BODY_SENSORS_WRIST_TEMPERATURE")
+ .setPermission(Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE)
+ .setDefaultMode(AppOpsManager.MODE_ALLOWED).build()
};
// The number of longs needed to form a full bitmask of app ops
diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java
index c19a865..20d19c1 100644
--- a/core/java/android/app/ForegroundServiceTypePolicy.java
+++ b/core/java/android/app/ForegroundServiceTypePolicy.java
@@ -350,6 +350,7 @@
new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
new RegularPermission(Manifest.permission.ACTIVITY_RECOGNITION),
new RegularPermission(Manifest.permission.BODY_SENSORS),
+ new RegularPermission(Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE),
new RegularPermission(Manifest.permission.HIGH_SAMPLING_RATE_SENSORS),
}, false)
);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 0866d94..9dc8ce6 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -779,6 +779,10 @@
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.DUMP)")
boolean isModernBroadcastQueueEnabled();
+ /** Checks if the process represented by the given pid is frozen. */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.DUMP)")
+ boolean isProcessFrozen(int pid);
+
/**
* @return The reason code of whether or not the given UID should be exempted from background
* restrictions here.
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index c58e627..dd44531 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -186,6 +186,7 @@
FLAG_IMMUTABLE,
FLAG_MUTABLE,
FLAG_MUTABLE_UNAUDITED,
+ FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT,
Intent.FILL_IN_ACTION,
Intent.FILL_IN_DATA,
@@ -280,6 +281,21 @@
public static final int FLAG_MUTABLE_UNAUDITED = FLAG_MUTABLE;
/**
+ * Flag indicating that the created PendingIntent with {@link #FLAG_MUTABLE}
+ * is allowed to have an unsafe implicit Intent within. <p>Starting with
+ * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, for apps that
+ * target SDK {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or
+ * higher, creation of a PendingIntent with {@link #FLAG_MUTABLE} and an
+ * implicit Intent within will throw an {@link IllegalArgumentException}
+ * for security reasons. To bypass this check, use
+ * {@link #FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT} when creating a PendingIntent.
+ * However, it is strongly recommended to not to use this flag and make the
+ * Intent explicit or the PendingIntent immutable, thereby making the Intent
+ * safe.
+ */
+ public static final int FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT = 1<<24;
+
+ /**
* Exception thrown when trying to send through a PendingIntent that
* has been canceled or is otherwise no longer able to execute the request.
*/
@@ -418,12 +434,13 @@
// This will be changed to a throw of an exception on the server side once we finish
// migrating to safer PendingIntents b/262253127.
// - Otherwise, warn that it will be blocked from target SDK U.
- if (isNewMutableImplicitPendingIntent(flags, intent)) {
+ if (isNewMutableDisallowedImplicitPendingIntent(flags, intent)) {
if (Compatibility.isChangeEnabled(BLOCK_MUTABLE_IMPLICIT_PENDING_INTENT)) {
String msg = packageName + ": Targeting U+ (version "
+ Build.VERSION_CODES.UPSIDE_DOWN_CAKE + " and above) disallows"
+ " creating or retrieving a PendingIntent with FLAG_MUTABLE,"
- + " an implicit Intent within and without FLAG_NO_CREATE for"
+ + " an implicit Intent within and without FLAG_NO_CREATE and"
+ + " FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT for"
+ " security reasons. To retrieve an already existing"
+ " PendingIntent, use FLAG_NO_CREATE, however, to create a"
+ " new PendingIntent with an implicit Intent use"
@@ -441,11 +458,15 @@
}
/** @hide */
- public static boolean isNewMutableImplicitPendingIntent(int flags, @NonNull Intent intent) {
+ public static boolean isNewMutableDisallowedImplicitPendingIntent(int flags,
+ @NonNull Intent intent) {
boolean isFlagNoCreateSet = (flags & PendingIntent.FLAG_NO_CREATE) != 0;
boolean isFlagMutableSet = (flags & PendingIntent.FLAG_MUTABLE) != 0;
boolean isImplicit = (intent.getComponent() == null) && (intent.getPackage() == null);
- return !isFlagNoCreateSet && isFlagMutableSet && isImplicit;
+ boolean isFlagAllowUnsafeImplicitIntentSet =
+ (flags & PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT) != 0;
+ return !isFlagNoCreateSet && isFlagMutableSet && isImplicit
+ && !isFlagAllowUnsafeImplicitIntentSet;
}
/**
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 5a153ce..e8f0a89 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -339,10 +339,17 @@
final ProviderInfo cpi = mContext.getPackageManager()
.resolveContentProvider(uri.getAuthority(),
PackageManager.ComponentInfoFlags.of(PackageManager.GET_META_DATA));
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ final Uri userUri = (mSingleUser
+ && !UserHandle.isSameUser(mMyUid, callingUid))
+ ? maybeAddUserId(uri, callingUserId) : uri;
if (cpi.forceUriPermissions
&& mInterface.checkUriPermission(uri,
callingUid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
- != PermissionChecker.PERMISSION_GRANTED) {
+ != PermissionChecker.PERMISSION_GRANTED
+ && getContext().checkUriPermission(userUri, Binder.getCallingPid(),
+ callingUid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ != PackageManager.PERMISSION_GRANTED) {
FrameworkStatsLog.write(GET_TYPE_ACCESSED_WITHOUT_PERMISSION,
enumCheckUriPermission,
callingUid, uri.getAuthority(), type);
diff --git a/core/java/android/content/pm/BaseParceledListSlice.java b/core/java/android/content/pm/BaseParceledListSlice.java
index 1e0deff..37b1778 100644
--- a/core/java/android/content/pm/BaseParceledListSlice.java
+++ b/core/java/android/content/pm/BaseParceledListSlice.java
@@ -17,6 +17,7 @@
package android.content.pm;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.BadParcelableException;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
@@ -92,18 +93,20 @@
data.writeInt(i);
try {
retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0);
+ reply.readException();
+ while (i < N && reply.readInt() != 0) {
+ listElementClass = readVerifyAndAddElement(creator, reply, loader,
+ listElementClass);
+ if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1));
+ i++;
+ }
} catch (RemoteException e) {
- Log.w(TAG, "Failure retrieving array; only received " + i + " of " + N, e);
- return;
+ throw new BadParcelableException(
+ "Failure retrieving array; only received " + i + " of " + N, e);
+ } finally {
+ reply.recycle();
+ data.recycle();
}
- while (i < N && reply.readInt() != 0) {
- listElementClass = readVerifyAndAddElement(creator, reply, loader,
- listElementClass);
- if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1));
- i++;
- }
- reply.recycle();
- data.recycle();
}
}
@@ -201,22 +204,29 @@
+ Binder.getCallingPid() + ", sender=" + this);
}
- while (i < N && reply.dataSize() < MAX_IPC_SIZE) {
- reply.writeInt(1);
+ try {
+ reply.writeNoException();
+ while (i < N && reply.dataSize() < MAX_IPC_SIZE) {
+ reply.writeInt(1);
- final T parcelable = mList.get(i);
- verifySameType(listElementClass, parcelable.getClass());
- writeElement(parcelable, reply, callFlags);
+ final T parcelable = mList.get(i);
+ verifySameType(listElementClass, parcelable.getClass());
+ writeElement(parcelable, reply, callFlags);
- if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i));
- i++;
- }
- if (i < N) {
- if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N);
- reply.writeInt(0);
- } else {
- if (DEBUG) Log.d(TAG, "Transfer complete, clearing mList reference");
+ if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i));
+ i++;
+ }
+ if (i < N) {
+ if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N);
+ reply.writeInt(0);
+ } else {
+ if (DEBUG) Log.d(TAG, "Transfer done, clearing mList reference");
+ mList = null;
+ }
+ } catch (RuntimeException e) {
+ if (DEBUG) Log.d(TAG, "Transfer failed, clearing mList reference");
mList = null;
+ throw e;
}
return true;
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e8a355f..5209c14 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2400,6 +2400,14 @@
public static final int DELETE_FAILED_APP_PINNED = -7;
/**
+ * Deletion failed return code: this is passed to the
+ * {@link IPackageDeleteObserver} if the system failed to delete the package
+ * for any child profile with {@link UserProperties#getDeleteAppWithParent()} as true.
+ * @hide
+ */
+ public static final int DELETE_FAILED_FOR_CHILD_PROFILE = -8;
+
+ /**
* Return code that is passed to the {@link IPackageMoveObserver} when the
* package has been successfully moved by the system.
*
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index a408ea6..f3209f9 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -323,6 +323,7 @@
* permissions:
* {@link android.Manifest.permission#ACTIVITY_RECOGNITION},
* {@link android.Manifest.permission#BODY_SENSORS},
+ * {@link android.Manifest.permission#BODY_SENSORS_WRIST_TEMPERATURE},
* {@link android.Manifest.permission#HIGH_SAMPLING_RATE_SENSORS}.
*/
@RequiresPermission(
@@ -332,6 +333,7 @@
anyOf = {
Manifest.permission.ACTIVITY_RECOGNITION,
Manifest.permission.BODY_SENSORS,
+ Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE,
Manifest.permission.HIGH_SAMPLING_RATE_SENSORS,
}
)
diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java
index 824d15c..77b1954 100644
--- a/core/java/android/content/pm/UserProperties.java
+++ b/core/java/android/content/pm/UserProperties.java
@@ -61,6 +61,7 @@
"mediaSharedWithParent";
private static final String ATTR_CREDENTIAL_SHAREABLE_WITH_PARENT =
"credentialShareableWithParent";
+ private static final String ATTR_DELETE_APP_WITH_PARENT = "deleteAppWithParent";
/** Index values of each property (to indicate whether they are present in this object). */
@IntDef(prefix = "INDEX_", value = {
@@ -73,7 +74,8 @@
INDEX_CROSS_PROFILE_INTENT_FILTER_ACCESS_CONTROL,
INDEX_CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY,
INDEX_MEDIA_SHARED_WITH_PARENT,
- INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT
+ INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT,
+ INDEX_DELETE_APP_WITH_PARENT,
})
@Retention(RetentionPolicy.SOURCE)
private @interface PropertyIndex {
@@ -88,6 +90,7 @@
private static final int INDEX_CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY = 7;
private static final int INDEX_MEDIA_SHARED_WITH_PARENT = 8;
private static final int INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT = 9;
+ private static final int INDEX_DELETE_APP_WITH_PARENT = 10;
/** A bit set, mapping each PropertyIndex to whether it is present (1) or absent (0). */
private long mPropertiesPresent = 0;
@@ -312,6 +315,7 @@
setCrossProfileIntentFilterAccessControl(
orig.getCrossProfileIntentFilterAccessControl());
setCrossProfileIntentResolutionStrategy(orig.getCrossProfileIntentResolutionStrategy());
+ setDeleteAppWithParent(orig.getDeleteAppWithParent());
}
if (hasManagePermission) {
// Add items that require MANAGE_USERS or stronger.
@@ -418,6 +422,24 @@
private boolean mStartWithParent;
/**
+ * Returns whether an app in the profile should be deleted when the same package in
+ * the parent user is being deleted.
+ * This only applies for users that have parents (i.e. for profiles).
+ * @hide
+ */
+ public boolean getDeleteAppWithParent() {
+ if (isPresent(INDEX_DELETE_APP_WITH_PARENT)) return mDeleteAppWithParent;
+ if (mDefaultProperties != null) return mDefaultProperties.mDeleteAppWithParent;
+ throw new SecurityException("You don't have permission to query deleteAppWithParent");
+ }
+ /** @hide */
+ public void setDeleteAppWithParent(boolean val) {
+ this.mDeleteAppWithParent = val;
+ setPresent(INDEX_DELETE_APP_WITH_PARENT);
+ }
+ private boolean mDeleteAppWithParent;
+
+ /**
* Return whether, and how, select user restrictions or device policies should be inherited
* from other user.
*
@@ -609,6 +631,7 @@
+ getCrossProfileIntentResolutionStrategy()
+ ", mMediaSharedWithParent=" + isMediaSharedWithParent()
+ ", mCredentialShareableWithParent=" + isCredentialShareableWithParent()
+ + ", mDeleteAppWithParent=" + getDeleteAppWithParent()
+ "}";
}
@@ -634,6 +657,7 @@
pw.println(prefix + " mMediaSharedWithParent=" + isMediaSharedWithParent());
pw.println(prefix + " mCredentialShareableWithParent="
+ isCredentialShareableWithParent());
+ pw.println(prefix + " mDeleteAppWithParent=" + getDeleteAppWithParent());
}
/**
@@ -697,6 +721,9 @@
case ATTR_CREDENTIAL_SHAREABLE_WITH_PARENT:
setCredentialShareableWithParent(parser.getAttributeBoolean(i));
break;
+ case ATTR_DELETE_APP_WITH_PARENT:
+ setDeleteAppWithParent(parser.getAttributeBoolean(i));
+ break;
default:
Slog.w(LOG_TAG, "Skipping unknown property " + attributeName);
}
@@ -752,6 +779,10 @@
serializer.attributeBoolean(null, ATTR_CREDENTIAL_SHAREABLE_WITH_PARENT,
mCredentialShareableWithParent);
}
+ if (isPresent(INDEX_DELETE_APP_WITH_PARENT)) {
+ serializer.attributeBoolean(null, ATTR_DELETE_APP_WITH_PARENT,
+ mDeleteAppWithParent);
+ }
}
// For use only with an object that has already had any permission-lacking fields stripped out.
@@ -768,6 +799,7 @@
dest.writeInt(mCrossProfileIntentResolutionStrategy);
dest.writeBoolean(mMediaSharedWithParent);
dest.writeBoolean(mCredentialShareableWithParent);
+ dest.writeBoolean(mDeleteAppWithParent);
}
/**
@@ -788,6 +820,7 @@
mCrossProfileIntentResolutionStrategy = source.readInt();
mMediaSharedWithParent = source.readBoolean();
mCredentialShareableWithParent = source.readBoolean();
+ mDeleteAppWithParent = source.readBoolean();
}
@Override
@@ -825,6 +858,7 @@
CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_DEFAULT;
private boolean mMediaSharedWithParent = false;
private boolean mCredentialShareableWithParent = false;
+ private boolean mDeleteAppWithParent = false;
public Builder setShowInLauncher(@ShowInLauncher int showInLauncher) {
mShowInLauncher = showInLauncher;
@@ -886,6 +920,12 @@
return this;
}
+ /** Sets the value for {@link #mDeleteAppWithParent}*/
+ public Builder setDeleteAppWithParent(boolean deleteAppWithParent) {
+ mDeleteAppWithParent = deleteAppWithParent;
+ return this;
+ }
+
/** Builds a UserProperties object with *all* values populated. */
public UserProperties build() {
return new UserProperties(
@@ -898,7 +938,8 @@
mCrossProfileIntentFilterAccessControl,
mCrossProfileIntentResolutionStrategy,
mMediaSharedWithParent,
- mCredentialShareableWithParent);
+ mCredentialShareableWithParent,
+ mDeleteAppWithParent);
}
} // end Builder
@@ -912,8 +953,8 @@
@CrossProfileIntentFilterAccessControlLevel int crossProfileIntentFilterAccessControl,
@CrossProfileIntentResolutionStrategy int crossProfileIntentResolutionStrategy,
boolean mediaSharedWithParent,
- boolean credentialShareableWithParent) {
-
+ boolean credentialShareableWithParent,
+ boolean deleteAppWithParent) {
mDefaultProperties = null;
setShowInLauncher(showInLauncher);
setStartWithParent(startWithParent);
@@ -925,5 +966,6 @@
setCrossProfileIntentResolutionStrategy(crossProfileIntentResolutionStrategy);
setMediaSharedWithParent(mediaSharedWithParent);
setCredentialShareableWithParent(credentialShareableWithParent);
+ setDeleteAppWithParent(deleteAppWithParent);
}
}
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 0def59f..335975b 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -98,6 +98,14 @@
/**
* Current user preference for the scaling factor for fonts, relative
* to the base density scaling.
+ *
+ * <p>Note: Please do not use this to hardcode font size equations. The equation for font
+ * scaling is now non-linear; this coefficient is no longer used as a direct multiplier to
+ * determine font size. It exists for informational purposes only.
+ *
+ * <p>Please use {@link android.util.TypedValue#applyDimension(int, float, DisplayMetrics)} or
+ * {@link android.util.TypedValue#deriveDimension(int, float, DisplayMetrics)} to convert
+ * between scaled font size dimensions and pixels.
*/
public float fontScale;
diff --git a/core/java/android/credentials/CredentialDescription.java b/core/java/android/credentials/CredentialDescription.java
index ec6a396..1a2a0e5 100644
--- a/core/java/android/credentials/CredentialDescription.java
+++ b/core/java/android/credentials/CredentialDescription.java
@@ -17,7 +17,6 @@
package android.credentials;
import android.annotation.NonNull;
-import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.service.credentials.CredentialEntry;
@@ -31,9 +30,7 @@
/**
* Represents the type and contained data fields of a {@link Credential}.
- * @hide
*/
-@TestApi
public final class CredentialDescription implements Parcelable {
/**
@@ -59,8 +56,11 @@
*
* @param type the type of the credential returned.
* @param flattenedRequestString flattened JSON string that will be matched with requests.
- * @param credentialEntries a list of {@link CredentialEntry}s that have been returned
- * to the developer upon credential creation.
+ * @param credentialEntries a list of {@link CredentialEntry}s that are to be shown on the
+ * account selector if a credential matches with this description.
+ * Each entry contains information to be displayed within an
+ * entry on the UI, as well as a {@link android.app.PendingIntent}
+ * that will be invoked if the user selects this entry.
*
* @throws IllegalArgumentException If type is empty.
*/
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index 232d063..1db14a3 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -23,7 +23,6 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
-import android.annotation.TestApi;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
@@ -314,53 +313,37 @@
}
/**
- * Registers a {@link CredentialDescription} for an actively provisioned {@link Credential}
- * a CredentialProvider has. This registry will then be used by
- * {@link #executeGetCredential(GetCredentialRequest, Activity,
- * CancellationSignal, Executor, OutcomeReceiver)} to determine where to
- * fetch the requested {@link Credential} from.
- *
+ * Registers a {@link CredentialDescription} for an actively provisioned {@link Credential}
+ * a CredentialProvider has. This registry will then be used to determine where to
+ * fetch the requested {@link Credential} from. Not all credential types will be supported.
+ * The distinction will be made by the JetPack layer. For the types that are supported,
+ * JetPack will add a new key-value pair into {@link GetCredentialRequest}. These will not
+ * be persistent on the device. The Credential Providers will need to call this API again
+ * upon device reboot.
*
* @param request the request data
- * @param cancellationSignal an optional signal that allows for cancelling this call
- * @param executor the callback will take place on this {@link Executor}
- * @param callback the callback invoked when the request succeeds or fails
*
* @throws {@link UnsupportedOperationException} if the feature has not been enabled.
+ * @throws {@link com.android.server.credentials.NonCredentialProviderCallerException}
+ * if the calling package name is not also listed as a Credential Provider.
+ * @throws {@link IllegalArgumentException} if the calling Credential Provider can not handle
+ * one or more of the Credential Types that are sent for registration.
*
- * @hide
*/
- @TestApi
public void registerCredentialDescription(
- @NonNull RegisterCredentialDescriptionRequest request,
- @Nullable CancellationSignal cancellationSignal,
- @CallbackExecutor @NonNull Executor executor,
- @NonNull OutcomeReceiver<Void, RegisterCredentialDescriptionException> callback) {
+ @NonNull RegisterCredentialDescriptionRequest request) {
if (!isCredentialDescriptionApiEnabled()) {
throw new UnsupportedOperationException("This API is not currently supported.");
}
- requireNonNull(executor, "executor must not be null");
- requireNonNull(callback, "callback must not be null");
+ requireNonNull(request, "request must not be null");
- if (cancellationSignal != null && cancellationSignal.isCanceled()) {
- Log.w(TAG, "executeCreateCredential already canceled");
- return;
- }
-
- ICancellationSignal cancelRemote = null;
try {
- cancelRemote = mService.registerCredentialDescription(request,
- new RegisterCredentialDescriptionTransport(executor, callback),
- mContext.getOpPackageName());
+ mService.registerCredentialDescription(request, mContext.getOpPackageName());
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
-
- if (cancellationSignal != null && cancelRemote != null) {
- cancellationSignal.setRemote(cancelRemote);
- }
}
@@ -370,45 +353,25 @@
*
*
* @param request the request data
- * @param cancellationSignal an optional signal that allows for cancelling this call
- * @param executor the callback will take place on this {@link Executor}
- * @param callback the callback invoked when the request succeeds or fails
*
* @throws {@link UnsupportedOperationException} if the feature has not been enabled.
*
- * @hide
*/
- @TestApi
- public void unRegisterCredentialDescription(
- @NonNull UnregisterCredentialDescriptionRequest request,
- @Nullable CancellationSignal cancellationSignal,
- @CallbackExecutor @NonNull Executor executor,
- @NonNull OutcomeReceiver<Void, UnregisterCredentialDescriptionException> callback) {
+ public void unregisterCredentialDescription(
+ @NonNull UnregisterCredentialDescriptionRequest request) {
if (!isCredentialDescriptionApiEnabled()) {
throw new UnsupportedOperationException("This API is not currently supported.");
}
- requireNonNull(executor, "executor must not be null");
- requireNonNull(callback, "callback must not be null");
+ requireNonNull(request, "request must not be null");
- if (cancellationSignal != null && cancellationSignal.isCanceled()) {
- Log.w(TAG, "executeCreateCredential already canceled");
- return;
- }
-
- ICancellationSignal cancelRemote = null;
try {
- cancelRemote = mService.unRegisterCredentialDescription(request,
- new UnregisterCredentialDescriptionTransport(executor, callback),
- mContext.getOpPackageName());
+ mService.unregisterCredentialDescription(request, mContext.getOpPackageName());
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
- if (cancellationSignal != null && cancelRemote != null) {
- cancellationSignal.setRemote(cancelRemote);
- }
}
private static class GetCredentialTransport extends IGetCredentialCallback.Stub {
@@ -572,54 +535,4 @@
() -> mCallback.onError(new SetEnabledProvidersException(errorType, message)));
}
}
-
- private static class RegisterCredentialDescriptionTransport
- extends IRegisterCredentialDescriptionCallback.Stub {
-
- private final Executor mExecutor;
- private final OutcomeReceiver<Void, RegisterCredentialDescriptionException> mCallback;
-
- private RegisterCredentialDescriptionTransport(Executor executor,
- OutcomeReceiver<Void, RegisterCredentialDescriptionException> callback) {
- mExecutor = executor;
- mCallback = callback;
- }
-
- @Override
- public void onResponse() {
- mCallback.onResult(null);
- }
-
- @Override
- public void onError(String errorCode, String message) {
- mExecutor.execute(
- () -> mCallback.onError(new RegisterCredentialDescriptionException(errorCode,
- message)));
- }
- }
-
- private static class UnregisterCredentialDescriptionTransport
- extends IUnregisterCredentialDescriptionCallback.Stub {
-
- private final Executor mExecutor;
- private final OutcomeReceiver<Void, UnregisterCredentialDescriptionException> mCallback;
-
- private UnregisterCredentialDescriptionTransport(Executor executor,
- OutcomeReceiver<Void, UnregisterCredentialDescriptionException> callback) {
- mExecutor = executor;
- mCallback = callback;
- }
-
- @Override
- public void onResponse() {
- mCallback.onResult(null);
- }
-
- @Override
- public void onError(String errorCode, String message) {
- mExecutor.execute(
- () -> mCallback.onError(new UnregisterCredentialDescriptionException(errorCode,
- message)));
- }
- }
}
diff --git a/core/java/android/credentials/GetCredentialOption.java b/core/java/android/credentials/GetCredentialOption.java
index 55daf86..f2895c7 100644
--- a/core/java/android/credentials/GetCredentialOption.java
+++ b/core/java/android/credentials/GetCredentialOption.java
@@ -32,6 +32,14 @@
public final class GetCredentialOption implements Parcelable {
/**
+ * Bundle key to the flattened version of the JSON request string. Framework will use this key
+ * to determine which types of Credentials will utilize Credential Registry when filtering
+ * Credential Providers to ping.
+ */
+ public static final String FLATTENED_REQUEST = "android.credentials"
+ + ".GetCredentialOption.FLATTENED_REQUEST_STRING";
+
+ /**
* The requested credential type.
*/
@NonNull
diff --git a/core/java/android/credentials/ICredentialManager.aidl b/core/java/android/credentials/ICredentialManager.aidl
index 75b3d0c..4ca3124 100644
--- a/core/java/android/credentials/ICredentialManager.aidl
+++ b/core/java/android/credentials/ICredentialManager.aidl
@@ -27,8 +27,6 @@
import android.credentials.ICreateCredentialCallback;
import android.credentials.IGetCredentialCallback;
import android.credentials.IListEnabledProvidersCallback;
-import android.credentials.IRegisterCredentialDescriptionCallback;
-import android.credentials.IUnregisterCredentialDescriptionCallback;
import android.credentials.ISetEnabledProvidersCallback;
import android.os.ICancellationSignal;
@@ -49,8 +47,8 @@
void setEnabledProviders(in List<String> providers, in int userId, in ISetEnabledProvidersCallback callback);
- @nullable ICancellationSignal registerCredentialDescription(in RegisterCredentialDescriptionRequest request, in IRegisterCredentialDescriptionCallback callback, String callingPackage);
+ void registerCredentialDescription(in RegisterCredentialDescriptionRequest request, String callingPackage);
- @nullable ICancellationSignal unRegisterCredentialDescription(in UnregisterCredentialDescriptionRequest request, in IUnregisterCredentialDescriptionCallback callback, String callingPackage);
+ void unregisterCredentialDescription(in UnregisterCredentialDescriptionRequest request, String callingPackage);
}
diff --git a/core/java/android/credentials/IUnregisterCredentialDescriptionCallback.aidl b/core/java/android/credentials/IUnregisterCredentialDescriptionCallback.aidl
deleted file mode 100644
index b30a12a..0000000
--- a/core/java/android/credentials/IUnregisterCredentialDescriptionCallback.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.credentials;
-
-/**
- * Listener for an registerCredentialDescription request.
- *
- * @hide
- */
-interface IUnregisterCredentialDescriptionCallback {
- oneway void onResponse();
- oneway void onError(String errorCode, String message);
-}
\ No newline at end of file
diff --git a/core/java/android/credentials/RegisterCredentialDescriptionException.java b/core/java/android/credentials/RegisterCredentialDescriptionException.java
deleted file mode 100644
index d19bb8e..0000000
--- a/core/java/android/credentials/RegisterCredentialDescriptionException.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.credentials;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.TestApi;
-import android.os.CancellationSignal;
-import android.os.OutcomeReceiver;
-
-import com.android.internal.util.Preconditions;
-
-import java.util.concurrent.Executor;
-
-/**
- * Represents an error encountered during the {@link
- * CredentialManager#registerCredentialDescription(RegisterCredentialDescriptionRequest,
- * CancellationSignal, Executor, OutcomeReceiver)} operation.
- *
- * @hide
- */
-@TestApi
-public class RegisterCredentialDescriptionException extends Exception {
-
- @NonNull public final String errorType;
-
- /**
- * Constructs a {@link RegisterCredentialDescriptionException}.
- *
- * @throws IllegalArgumentException If errorType is empty.
- */
- public RegisterCredentialDescriptionException(@NonNull String errorType,
- @Nullable String message) {
- this(errorType, message, null);
- }
-
- /**
- * Constructs a {@link RegisterCredentialDescriptionException}.
- *
- * @throws IllegalArgumentException If errorType is empty.
- */
- public RegisterCredentialDescriptionException(
- @NonNull String errorType, @Nullable String message, @Nullable Throwable cause) {
- super(message, cause);
- this.errorType =
- Preconditions
- .checkStringNotEmpty(errorType, "errorType must not be empty");
- }
-
- /**
- * Constructs a {@link RegisterCredentialDescriptionException}.
- *
- * @throws IllegalArgumentException If errorType is empty.
- */
- public RegisterCredentialDescriptionException(@NonNull String errorType,
- @Nullable Throwable cause) {
- this(errorType, null, cause);
- }
-
- /**
- * Constructs a {@link RegisterCredentialDescriptionException}.
- *
- * @throws IllegalArgumentException If errorType is empty.
- */
- public RegisterCredentialDescriptionException(@NonNull String errorType) {
- this(errorType, null, null);
- }
-}
diff --git a/core/java/android/credentials/RegisterCredentialDescriptionRequest.java b/core/java/android/credentials/RegisterCredentialDescriptionRequest.java
index f257ac5..b5f3610 100644
--- a/core/java/android/credentials/RegisterCredentialDescriptionRequest.java
+++ b/core/java/android/credentials/RegisterCredentialDescriptionRequest.java
@@ -19,7 +19,6 @@
import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
-import android.annotation.TestApi;
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
@@ -28,20 +27,18 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* A request to register a {@link ComponentName} that contains an actively provisioned
* {@link Credential} represented by a {@link CredentialDescription}.
*
- * @hide
*/
-@TestApi
public final class RegisterCredentialDescriptionRequest implements Parcelable {
- public static final String FLATTENED_REQUEST_STRING_KEY = "flattened_request_string";
-
@NonNull
private final List<CredentialDescription> mCredentialDescriptions;
@@ -51,7 +48,7 @@
}
public RegisterCredentialDescriptionRequest(
- @NonNull List<CredentialDescription> credentialDescriptions) {
+ @NonNull Set<CredentialDescription> credentialDescriptions) {
mCredentialDescriptions = new ArrayList<>(requireNonNull(credentialDescriptions));
}
@@ -89,7 +86,7 @@
}
@NonNull
- public List<CredentialDescription> getCredentialDescriptions() {
- return mCredentialDescriptions;
+ public Set<CredentialDescription> getCredentialDescriptions() {
+ return new HashSet<>(mCredentialDescriptions);
}
}
diff --git a/core/java/android/credentials/UnregisterCredentialDescriptionException.java b/core/java/android/credentials/UnregisterCredentialDescriptionException.java
deleted file mode 100644
index a16915a..0000000
--- a/core/java/android/credentials/UnregisterCredentialDescriptionException.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.credentials;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.TestApi;
-import android.os.CancellationSignal;
-import android.os.OutcomeReceiver;
-
-import com.android.internal.util.Preconditions;
-
-import java.util.concurrent.Executor;
-
-/**
- * Represents an error encountered during the {@link
- * CredentialManager#registerCredentialDescription(RegisterCredentialDescriptionRequest,
- * CancellationSignal, Executor, OutcomeReceiver)} operation.
- *
- * @hide
- */
-@TestApi
-public class UnregisterCredentialDescriptionException extends Exception {
-
- @NonNull public final String errorType;
-
- /**
- * Constructs a {@link RegisterCredentialDescriptionException}.
- *
- * @throws IllegalArgumentException If errorType is empty.
- */
- public UnregisterCredentialDescriptionException(@NonNull String errorType,
- @Nullable String message) {
- this(errorType, message, null);
- }
-
- /**
- * Constructs a {@link RegisterCredentialDescriptionException}.
- *
- * @throws IllegalArgumentException If errorType is empty.
- */
- public UnregisterCredentialDescriptionException(
- @NonNull String errorType, @Nullable String message, @Nullable Throwable cause) {
- super(message, cause);
- this.errorType =
- Preconditions
- .checkStringNotEmpty(errorType, "errorType must not be empty");
- }
-
- /**
- * Constructs a {@link RegisterCredentialDescriptionException}.
- *
- * @throws IllegalArgumentException If errorType is empty.
- */
- public UnregisterCredentialDescriptionException(@NonNull String errorType,
- @Nullable Throwable cause) {
- this(errorType, null, cause);
- }
-
- /**
- * Constructs a {@link RegisterCredentialDescriptionException}.
- *
- * @throws IllegalArgumentException If errorType is empty.
- */
- public UnregisterCredentialDescriptionException(@NonNull String errorType) {
- this(errorType, null, null);
- }
-}
diff --git a/core/java/android/credentials/UnregisterCredentialDescriptionRequest.java b/core/java/android/credentials/UnregisterCredentialDescriptionRequest.java
index 6cf40e7..cfda474 100644
--- a/core/java/android/credentials/UnregisterCredentialDescriptionRequest.java
+++ b/core/java/android/credentials/UnregisterCredentialDescriptionRequest.java
@@ -19,38 +19,45 @@
import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
-import android.annotation.TestApi;
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.util.AnnotationValidations;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
/**
* A request to unregister a {@link ComponentName} that contains an actively provisioned
* {@link Credential} represented by a {@link CredentialDescription}. *
*
- * @hide
*/
-@TestApi
public final class UnregisterCredentialDescriptionRequest implements Parcelable {
@NonNull
- private final CredentialDescription mCredentialDescription;
+ private final List<CredentialDescription> mCredentialDescriptions;
- public UnregisterCredentialDescriptionRequest(@NonNull CredentialDescription
- credentialDescription) {
- mCredentialDescription = requireNonNull(credentialDescription);
+ public UnregisterCredentialDescriptionRequest(
+ @NonNull CredentialDescription credentialDescription) {
+ mCredentialDescriptions = Arrays.asList(requireNonNull(credentialDescription));
+ }
+
+ public UnregisterCredentialDescriptionRequest(
+ @NonNull List<CredentialDescription> credentialDescriptions) {
+ mCredentialDescriptions = new ArrayList<>(requireNonNull(credentialDescriptions));
}
private UnregisterCredentialDescriptionRequest(@NonNull Parcel in) {
- CredentialDescription credentialDescription =
- CredentialDescription.CREATOR.createFromParcel(in);
+ List<CredentialDescription> credentialDescriptions = new ArrayList<>();
+ in.readTypedList(credentialDescriptions, CredentialDescription.CREATOR);
- mCredentialDescription = credentialDescription;
+ mCredentialDescriptions = new ArrayList<>();
AnnotationValidations.validate(android.annotation.NonNull.class, null,
- credentialDescription);
+ credentialDescriptions);
+ mCredentialDescriptions.addAll(credentialDescriptions);
}
public static final @NonNull Parcelable.Creator<UnregisterCredentialDescriptionRequest>
@@ -73,11 +80,11 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- mCredentialDescription.writeToParcel(dest, flags);
+ dest.writeTypedList(mCredentialDescriptions, flags);
}
@NonNull
- public CredentialDescription getCredentialDescription() {
- return mCredentialDescription;
+ public List<CredentialDescription> getCredentialDescriptions() {
+ return mCredentialDescriptions;
}
}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index b333f5a..08238ca 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -1301,6 +1301,54 @@
}
/**
+ * Sets the HDR conversion mode for the device.
+ *
+ * @param hdrConversionMode The {@link HdrConversionMode} to set.
+ * Note, {@code HdrConversionMode.preferredHdrOutputType} is only applicable when
+ * {@code HdrConversionMode.conversionMode} is {@link HdrConversionMode#HDR_CONVERSION_FORCE}.
+ *
+ * @throws IllegalArgumentException if hdrConversionMode.preferredHdrOutputType is not set
+ * when hdrConversionMode.conversionMode is {@link HdrConversionMode#HDR_CONVERSION_FORCE}.
+ * @throws IllegalArgumentException if hdrConversionMode.preferredHdrOutputType is set but
+ * hdrConversionMode.conversionMode is not {@link HdrConversionMode#HDR_CONVERSION_FORCE}.
+ *
+ * @see #getHdrConversionMode
+ * @see #getSupportedHdrOutputTypes
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.MODIFY_HDR_CONVERSION_MODE)
+ public void setHdrConversionMode(@NonNull HdrConversionMode hdrConversionMode) {
+ mGlobal.setHdrConversionMode(hdrConversionMode);
+ }
+
+ /**
+ * Returns the {@link HdrConversionMode} of the device, which is set by the user.
+ *
+ * @see #setHdrConversionMode
+ * @see #getSupportedHdrOutputTypes
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ public HdrConversionMode getHdrConversionMode() {
+ return mGlobal.getHdrConversionMode();
+ }
+
+ /**
+ * Returns the HDR output types supported by the device.
+ *
+ * @see #getHdrConversionMode
+ * @see #setHdrConversionMode
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ public @HdrType int[] getSupportedHdrOutputTypes() {
+ return mGlobal.getSupportedHdrOutputTypes();
+ }
+
+ /**
* When enabled the app requested mode is always selected regardless of user settings and
* policies for low brightness, low battery, etc.
*
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index f038c66..d9db177 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -979,6 +979,39 @@
}
/**
+ * Sets the {@link HdrConversionMode} for the device.
+ */
+ public void setHdrConversionMode(@NonNull HdrConversionMode hdrConversionMode) {
+ try {
+ mDm.setHdrConversionMode(hdrConversionMode);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the {@link HdrConversionMode} of the device.
+ */
+ public HdrConversionMode getHdrConversionMode() {
+ try {
+ return mDm.getHdrConversionMode();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the HDR output types supported by the device.
+ */
+ public @HdrType int[] getSupportedHdrOutputTypes() {
+ try {
+ return mDm.getSupportedHdrOutputTypes();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* When enabled the app requested display resolution and refresh rate is always selected
* in DisplayModeDirector regardless of user settings and policies for low brightness, low
* battery etc.
diff --git a/core/java/android/credentials/IRegisterCredentialDescriptionCallback.aidl b/core/java/android/hardware/display/HdrConversionMode.aidl
similarity index 65%
rename from core/java/android/credentials/IRegisterCredentialDescriptionCallback.aidl
rename to core/java/android/hardware/display/HdrConversionMode.aidl
index 124a319..ac89dd6 100644
--- a/core/java/android/credentials/IRegisterCredentialDescriptionCallback.aidl
+++ b/core/java/android/hardware/display/HdrConversionMode.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 The Android Open Source Project
+ * 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.
@@ -14,14 +14,6 @@
* limitations under the License.
*/
-package android.credentials;
+package android.hardware.display;
-/**
- * Listener for an registerCredentialDescription request.
- *
- * @hide
- */
-interface IRegisterCredentialDescriptionCallback {
- oneway void onResponse();
- oneway void onError(String errorCode, String message);
-}
\ No newline at end of file
+parcelable HdrConversionMode;
\ No newline at end of file
diff --git a/core/java/android/hardware/display/HdrConversionMode.java b/core/java/android/hardware/display/HdrConversionMode.java
new file mode 100644
index 0000000..1accd17
--- /dev/null
+++ b/core/java/android/hardware/display/HdrConversionMode.java
@@ -0,0 +1,122 @@
+/*
+ * 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 android.hardware.display;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.Display;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Describes the HDR conversion mode for a device.
+ *
+ * This class is used when user changes the HDR conversion mode of the device via
+ * {@link DisplayManager#setHdrConversionMode(HdrConversionMode)}.
+ * <p>
+ * HDR conversion mode has a conversionMode and preferredHdrOutputType. </p><p>
+ * The conversionMode can be one of:
+ * HDR_CONVERSION_PASSSTHROUGH : HDR conversion is disabled. The output HDR type will change
+ * dynamically to match the content. In this mode, preferredHdrOutputType should not be set.
+ * HDR_CONVERSION_AUTO: The output HDR type is selected by the implementation. In this mode,
+ * preferredHdrOutputType should not be set.
+ * HDR_CONVERSION_FORCE : The implementation converts all content to this HDR type, when possible.
+ * In this mode, preferredHdrOutputType should be set.
+ * </p>
+ * @hide
+ */
+@TestApi
+public final class HdrConversionMode implements Parcelable {
+ /** HDR output conversion is disabled */
+ public static final int HDR_CONVERSION_PASSTHROUGH = 1;
+ /** HDR output conversion is managed by the device manufacturer's implementation. */
+ public static final int HDR_CONVERSION_SYSTEM = 2;
+ /**
+ * HDR output conversion is set by the user. The preferred output type must be
+ * set in this case.
+ */
+ public static final int HDR_CONVERSION_FORCE = 3;
+
+ /** @hide */
+ @IntDef(prefix = {"HDR_CONVERSION"}, value = {
+ HDR_CONVERSION_PASSTHROUGH,
+ HDR_CONVERSION_SYSTEM,
+ HDR_CONVERSION_FORCE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ConversionMode {}
+
+ public static final @NonNull
+ Parcelable.Creator<HdrConversionMode> CREATOR =
+ new Parcelable.Creator<>() {
+ @Override
+ public HdrConversionMode createFromParcel(Parcel source) {
+ return new HdrConversionMode(source);
+ }
+
+ @Override
+ public HdrConversionMode[] newArray(int size) {
+ return new HdrConversionMode[size];
+ }
+ };
+
+ private final @ConversionMode int mConversionMode;
+ private @Display.HdrCapabilities.HdrType int mPreferredHdrOutputType;
+
+ public HdrConversionMode(int conversionMode, int preferredHdrOutputType) {
+ if (conversionMode != HdrConversionMode.HDR_CONVERSION_FORCE
+ && preferredHdrOutputType != -1) {
+ throw new IllegalArgumentException("preferredHdrOutputType must not be set if"
+ + " the conversion mode is not HDR_CONVERSION_FORCE");
+ }
+
+ mConversionMode = conversionMode;
+ mPreferredHdrOutputType = preferredHdrOutputType;
+ }
+
+ public HdrConversionMode(int conversionMode) {
+ mConversionMode = conversionMode;
+ mPreferredHdrOutputType = Display.HdrCapabilities.HDR_TYPE_INVALID;
+ }
+
+ private HdrConversionMode(Parcel source) {
+ this(source.readInt(), source.readInt());
+ }
+
+ public int getConversionMode() {
+ return mConversionMode;
+ }
+
+ public int getPreferredHdrOutputType() {
+ return mPreferredHdrOutputType;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mConversionMode);
+ dest.writeInt(mPreferredHdrOutputType);
+ }
+}
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 28bb35f..0a44f85 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -23,6 +23,7 @@
import android.hardware.display.BrightnessInfo;
import android.hardware.display.Curve;
import android.hardware.graphics.common.DisplayDecorationSupport;
+import android.hardware.display.HdrConversionMode;
import android.hardware.display.IDisplayManagerCallback;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplayConfig;
@@ -174,6 +175,14 @@
Mode getUserPreferredDisplayMode(int displayId);
Mode getSystemPreferredDisplayMode(int displayId);
+ // Sets the HDR conversion mode for a device.
+ // Requires MODIFY_HDR_CONVERSION_MODE permission.
+ @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ + ".permission.MODIFY_HDR_CONVERSION_MODE)")
+ void setHdrConversionMode(in HdrConversionMode hdrConversionMode);
+ HdrConversionMode getHdrConversionMode();
+ int[] getSupportedHdrOutputTypes();
+
// When enabled the app requested display resolution and refresh rate is always selected
// in DisplayModeDirector regardless of user settings and policies for low brightness, low
// battery etc.
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index 101a071..25ee6af 100755
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -283,7 +283,13 @@
* A scaling factor for fonts displayed on the display. This is the same
* as {@link #density}, except that it may be adjusted in smaller
* increments at runtime based on a user preference for the font size.
+ *
+ * @deprecated this scalar factor is no longer accurate due to adaptive non-linear font scaling.
+ * Please use {@link TypedValue#applyDimension(int, float, DisplayMetrics)} or
+ * {@link TypedValue#deriveDimension(int, float, DisplayMetrics)} to convert between SP font
+ * sizes and pixels.
*/
+ @Deprecated
public float scaledDensity;
/**
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 689dce8..25863a6 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -2323,6 +2323,10 @@
*/
public static final float INVALID_LUMINANCE = -1;
/**
+ * Invalid HDR type value.
+ */
+ public static final int HDR_TYPE_INVALID = -1;
+ /**
* Dolby Vision high dynamic range (HDR) display.
*/
public static final int HDR_TYPE_DOLBY_VISION = 1;
@@ -2350,6 +2354,7 @@
/** @hide */
@IntDef(prefix = { "HDR_TYPE_" }, value = {
+ HDR_TYPE_INVALID,
HDR_TYPE_DOLBY_VISION,
HDR_TYPE_HDR10,
HDR_TYPE_HLG,
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index f6348d7..490091b 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -292,7 +292,7 @@
public SurfaceControlViewHost(@NonNull Context c, @NonNull Display d,
@NonNull WindowlessWindowManager wwm) {
mWm = wwm;
- mViewRoot = new ViewRootImpl(c, d, mWm);
+ mViewRoot = new ViewRootImpl(c, d, mWm, new WindowlessWindowLayout());
addConfigCallback(c, d);
WindowManagerGlobal.getInstance().addWindowlessRoot(mViewRoot);
@@ -322,7 +322,7 @@
mWm = new WindowlessWindowManager(context.getResources().getConfiguration(),
mSurfaceControl, hostToken);
- mViewRoot = new ViewRootImpl(context, display, mWm);
+ mViewRoot = new ViewRootImpl(context, display, mWm, new WindowlessWindowLayout());
addConfigCallback(context, display);
WindowManagerGlobal.getInstance().addWindowlessRoot(mViewRoot);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5165478..6eb932e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -930,13 +930,14 @@
private String mTag = TAG;
public ViewRootImpl(Context context, Display display) {
- this(context, display, WindowManagerGlobal.getWindowSession());
+ this(context, display, WindowManagerGlobal.getWindowSession(), new WindowLayout());
}
- public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session) {
+ public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,
+ WindowLayout windowLayout) {
mContext = context;
mWindowSession = session;
- mWindowLayout = new WindowLayout();
+ mWindowLayout = windowLayout;
mDisplay = display;
mBasePackageName = context.getBasePackageName();
mThread = Thread.currentThread();
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 5f6f55a..5144f3a 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -629,6 +629,114 @@
@interface DisplayImePolicy {}
/**
+ * The root state of all the state. This is an abstract state, and the keyguard should be in
+ * one of this sub state.
+ *
+ * @hide
+ */
+ int KEYGUARD_STATE_ROOT = 0x0;
+
+ /**
+ * Keyguard is off, so activity can be shown on the screen.
+ *
+ * @hide
+ */
+ int KEYGUARD_STATE_OFF = 0x1;
+
+ /**
+ * The keyguard is off, but lock screen is still rendered on the screen. Waiting for
+ * starting unlock animation.
+ *
+ * @hide
+ */
+ int KEYGUARD_STATE_GOING_AWAY = 0x11;
+
+ /**
+ * They keyguard is on, so normal activities cannot be shown on the screen. This is an abstract
+ * state, and the keyguard should be in one of ths sub state.
+ *
+ * @hide
+ */
+ int KEYGUARD_STATE_ON = 0x2;
+
+ /**
+ * The keyguard is on and not occluded.
+ * @hide
+ */
+ int KEYGUARD_STATE_KEYGUARD_TOP = 0x21;
+
+ /**
+ * The keyguard is on, and the lock screen is shown.
+ *
+ * @hide
+ */
+ int KEYGUARD_STATE_LOCKSCREEN_SHOWN = 0x211;
+
+ /**
+ * The keyguard is on, and the AOD is shown.
+ *
+ * @hide
+ */
+ int KEYGUARD_STATE_AOD_SHOWN = 0x212;
+
+ /**
+ * The keyguard is on but it's occluded by a normal SHOW_WHEN_LOCKED activity (i.e. non
+ * occluded by Dream activity).
+ *
+ * @hide
+ */
+ int KEYGUARD_STATE_OCCLUDED = 0x22;
+
+ /**
+ * The keyguard is on but it's occluded by a Dream activity.
+ *
+ * @hide
+ */
+ int KEYGUARD_STATE_DREAMING = 0x221;
+
+ /** @hide */
+ @IntDef(prefix = { "KEYGUARD_STATE_" }, value = {
+ KEYGUARD_STATE_ROOT,
+ KEYGUARD_STATE_OFF,
+ KEYGUARD_STATE_GOING_AWAY,
+ KEYGUARD_STATE_ON,
+ KEYGUARD_STATE_KEYGUARD_TOP,
+ KEYGUARD_STATE_LOCKSCREEN_SHOWN,
+ KEYGUARD_STATE_AOD_SHOWN,
+ KEYGUARD_STATE_OCCLUDED,
+ KEYGUARD_STATE_DREAMING,
+ })
+ @interface KeyguardState {}
+
+ /**
+ * @hide
+ */
+ static String keyguardStateToString(@KeyguardState int type) {
+ switch (type) {
+ case KEYGUARD_STATE_ROOT:
+ return "ROOT";
+ case KEYGUARD_STATE_OFF:
+ return "KEYGUARD_OFF";
+ case KEYGUARD_STATE_GOING_AWAY:
+ return "KEYGUARD_GOING_AWAY";
+ case KEYGUARD_STATE_ON:
+ return "KEYGUARD_ON";
+ case KEYGUARD_STATE_KEYGUARD_TOP:
+ return "KEYGUARD_TOP";
+ case KEYGUARD_STATE_LOCKSCREEN_SHOWN:
+ return "KEYGUARD_LOCKSCREEN_SHOWN";
+ case KEYGUARD_STATE_AOD_SHOWN:
+ return "KEYGUARD_AOD_SHOWN";
+ case KEYGUARD_STATE_OCCLUDED:
+ return "KEYGUARD_OCCLUDED";
+ case KEYGUARD_STATE_DREAMING:
+ return "KEYGUARD_DREAMING";
+ default:
+ return "KEYGUARD_STATE_UNKNOWN(" + Integer.toHexString(type) + ")";
+ }
+ }
+
+ /**
* Exception that is thrown when trying to add view whose
* {@link LayoutParams} {@link LayoutParams#token}
* is invalid.
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index acc0c0b..4a9dc5b 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -388,7 +388,8 @@
if (windowlessSession == null) {
root = new ViewRootImpl(view.getContext(), display);
} else {
- root = new ViewRootImpl(view.getContext(), display, windowlessSession);
+ root = new ViewRootImpl(view.getContext(), display,
+ windowlessSession, new WindowlessWindowLayout());
}
view.setLayoutParams(wparams);
diff --git a/core/java/android/view/WindowlessWindowLayout.java b/core/java/android/view/WindowlessWindowLayout.java
new file mode 100644
index 0000000..e2afaa5
--- /dev/null
+++ b/core/java/android/view/WindowlessWindowLayout.java
@@ -0,0 +1,71 @@
+/*
+ * 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 android.view;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import android.app.WindowConfiguration.WindowingMode;
+import android.graphics.Rect;
+import android.view.WindowInsets.Type.InsetsType;
+import android.window.ClientWindowFrames;
+
+/**
+ * Computes window frames for the windowless window.
+ *
+ * This can't be replaced with the regular WindowLayout because WindowLayout computes bounds
+ * with insets and cutout values. Since windowless windows aren't affected by insets and
+ * instead are bound by their parent, it will compute incorrect bounds for them if insets are used.
+ *
+ * @hide
+ */
+public class WindowlessWindowLayout extends WindowLayout {
+
+ @Override
+ public void computeFrames(WindowManager.LayoutParams attrs, InsetsState state,
+ Rect displayCutoutSafe, Rect windowBounds, @WindowingMode int windowingMode,
+ int requestedWidth, int requestedHeight, @InsetsType int requestedVisibleTypes,
+ float compatScale, ClientWindowFrames frames) {
+ if (frames.attachedFrame == null) {
+ frames.frame.set(0, 0, attrs.width, attrs.height);
+ frames.parentFrame.set(frames.frame);
+ frames.displayFrame.set(frames.frame);
+ return;
+ }
+
+ final int height = calculateLength(attrs.height, requestedHeight,
+ frames.attachedFrame.height());
+ final int width = calculateLength(attrs.width, requestedWidth,
+ frames.attachedFrame.width());
+ Gravity.apply(attrs.gravity, width, height, frames.attachedFrame,
+ (int) (attrs.x + attrs.horizontalMargin),
+ (int) (attrs.y + attrs.verticalMargin),
+ frames.frame);
+ frames.displayFrame.set(frames.frame);
+ frames.parentFrame.set(frames.attachedFrame);
+ }
+
+ private static int calculateLength(int attrLength, int requestedLength, int parentLength) {
+ if (attrLength == MATCH_PARENT) {
+ return parentLength;
+ }
+ if (attrLength == WRAP_CONTENT) {
+ return requestedLength;
+ }
+ return attrLength;
+ }
+}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 7d37c50..21d22f2 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -94,10 +94,7 @@
private InsetsState mInsetsState;
private final ClientWindowFrames mTmpFrames = new ClientWindowFrames();
private final MergedConfiguration mTmpConfig = new MergedConfiguration();
- private final InsetsState mTmpInsetsState = new InsetsState();
- private final Rect mTmpDisplayCutoutSafe = new Rect();
- private final Rect mTmpWindowBounds = new Rect();
- private final WindowLayout mLayout = new WindowLayout();
+ private final WindowlessWindowLayout mLayout = new WindowlessWindowLayout();
public WindowlessWindowManager(Configuration c, SurfaceControl rootSurface,
IBinder hostInputToken) {
@@ -349,27 +346,22 @@
}
WindowManager.LayoutParams attrs = state.mParams;
- mTmpFrames.attachedFrame = state.mAttachedFrame;
+ ClientWindowFrames frames = new ClientWindowFrames();
+ frames.attachedFrame = state.mAttachedFrame;
- if (state.mAttachedFrame == null) {
- mTmpWindowBounds.set(0, 0, requestedWidth, requestedHeight);
- } else {
- mTmpWindowBounds.set(state.mAttachedFrame);
- }
+ mLayout.computeFrames(attrs, null, null, null, WindowConfiguration.WINDOWING_MODE_UNDEFINED,
+ requestedWidth, requestedHeight, 0, 0,
+ frames);
- mLayout.computeFrames(attrs, mTmpInsetsState, mTmpDisplayCutoutSafe, mTmpWindowBounds,
- WindowConfiguration.WINDOWING_MODE_UNDEFINED, requestedWidth, requestedHeight, 0,
- 1f, mTmpFrames);
-
- state.mFrame.set(mTmpFrames.frame);
+ state.mFrame.set(frames.frame);
if (outFrames != null) {
- outFrames.frame.set(mTmpFrames.frame);
- outFrames.parentFrame.set(mTmpFrames.parentFrame);
- outFrames.displayFrame.set(mTmpFrames.displayFrame);
+ outFrames.frame.set(frames.frame);
+ outFrames.parentFrame.set(frames.parentFrame);
+ outFrames.displayFrame.set(frames.displayFrame);
}
- t.setPosition(leash, mTmpFrames.frame.left, mTmpFrames.frame.top);
- t.setWindowCrop(leash, mTmpFrames.frame.width(), mTmpFrames.frame.height());
+ t.setPosition(leash, frames.frame.left, frames.frame.top);
+ t.setWindowCrop(leash, frames.frame.width(), frames.frame.height());
if (viewFlags == View.VISIBLE) {
// TODO(b/262892794) ViewRootImpl modifies the app's rendering SurfaceControl
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 82e1777..b112e7a 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -117,7 +117,7 @@
repeated KeyguardOccludedProto keyguard_occluded_states = 2 [deprecated=true];
optional bool aod_showing = 3;
repeated KeyguardPerDisplayProto keyguard_per_display = 4;
- optional bool keyguard_going_away = 5;
+ optional bool keyguard_going_away = 5 [deprecated=true];
}
message KeyguardOccludedProto {
@@ -134,7 +134,7 @@
optional bool keyguard_showing = 2;
optional bool aod_showing = 3;
optional bool keyguard_occluded = 4;
- optional bool keyguard_going_away = 5;
+ optional bool keyguard_going_away = 5 [deprecated=true];
}
/* represents PhoneWindowManager */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 077b0c5..07aefbd 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1733,6 +1733,34 @@
android:protectionLevel="dangerous"
android:permissionFlags="hardRestricted" />
+ <!-- Allows an application to access wrist temperature data from the watch sensors.
+ <p class="note"><strong>Note: </strong> This permission is for Wear OS only.
+ <p>Protection level: dangerous -->
+ <permission android:name="android.permission.BODY_SENSORS_WRIST_TEMPERATURE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_bodySensorsWristTemperature"
+ android:description="@string/permdesc_bodySensorsWristTemperature"
+ android:backgroundPermission="android.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND"
+ android:protectionLevel="dangerous" />
+
+ <!-- Allows an application to access wrist temperature data from the watch sensors.
+ If you're requesting this permission, you must also request
+ {@link #BODY_SENSORS_WRIST_TEMPERATURE}. Requesting this permission by itself doesn't
+ give you heart rate body sensors access.
+ <p class="note"><strong>Note: </strong> This permission is for Wear OS only.
+ <p>Protection level: dangerous
+
+ <p> This is a hard restricted permission which cannot be held by an app until
+ the installer on record allowlists the permission. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
+ -->
+ <permission android:name="android.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_bodySensors_wristTemperature_background"
+ android:description="@string/permdesc_bodySensors_wristTemperature_background"
+ android:protectionLevel="dangerous"
+ android:permissionFlags="hardRestricted" />
+
<!-- Allows an app to use fingerprint hardware.
<p>Protection level: normal
@deprecated Applications should request {@link
@@ -5244,6 +5272,12 @@
<permission android:name="android.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE"
android:protectionLevel="signature" />
+ <!-- Allows an application to modify the HDR conversion mode.
+ @hide
+ @TestApi -->
+ <permission android:name="android.permission.MODIFY_HDR_CONVERSION_MODE"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows an application to control VPN.
<p>Not for use by third-party applications.</p>
@hide -->
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index 4b27bf2..fe296c7 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -18,7 +18,6 @@
<bool name="kg_enable_camera_default_widget">true</bool>
<bool name="kg_center_small_widgets_vertically">false</bool>
<bool name="kg_top_align_page_shrink_on_bouncer_visible">true</bool>
- <bool name="kg_wake_on_acquire_start">false</bool>
<bool name="action_bar_embed_tabs">true</bool>
<bool name="split_action_bar_is_narrow">true</bool>
<bool name="preferences_prefer_dual_pane">false</bool>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 7c6f81d..fa8ba9d 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1329,6 +1329,16 @@
<!-- Description of the background body sensors permission, listed so the user can decide whether to allow the application to access data from body sensors in the background. [CHAR LIMIT=NONE] -->
<string name="permdesc_bodySensors_background" product="default">Allows the app to access body sensor data, such as heart rate, temperature, and blood oxygen percentage, while the app is in the background.</string>
+ <!-- Title of the body sensors wrist temperature permission, listed so the user can decide whether to allow the application to access body sensor wrist temperature data. [CHAR LIMIT=NONE] -->
+ <string name="permlab_bodySensorsWristTemperature">Access body sensor wrist temperature data while the app is in use.</string>
+ <!-- Description of the body sensors wrist temperature permission, listed so the user can decide whether to allow the application to access data from body sensors. [CHAR LIMIT=NONE] -->
+ <string name="permdesc_bodySensorsWristTemperature" product="default">Allows the app to access body sensor wrist temperature data, while the app is in use.</string>
+
+ <!-- Title of the body sensors wrist temperature permission, listed so the user can decide whether to allow the application to access body sensor wrist temperature data. [CHAR LIMIT=NONE] -->
+ <string name="permlab_bodySensors_wristTemperature_background">Access body sensor wrist temperature data while the app is in the background.</string>
+ <!-- Description of the body sensors wrist temperature permission, listed so the user can decide whether to allow the application to access data from body sensors. [CHAR LIMIT=NONE] -->
+ <string name="permdesc_bodySensors_wristTemperature_background" product="default">Allows the app to access body sensor wrist temperature data, while the app is in the background.</string>
+
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_readCalendar">Read calendar events and details</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 38336e4..9dc7835 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2774,7 +2774,6 @@
<java-symbol type="dimen" name="fast_scroller_minimum_touch_target" />
<java-symbol type="array" name="config_cdma_international_roaming_indicators" />
<java-symbol type="string" name="kg_text_message_separator" />
- <java-symbol type="bool" name="kg_wake_on_acquire_start" />
<java-symbol type="bool" name="config_use_sim_language_file" />
<java-symbol type="bool" name="config_LTE_eri_for_network_name" />
diff --git a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
index 01907fb..dea1b0e 100644
--- a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
+++ b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
@@ -16,8 +16,11 @@
package android.content.pm;
+import static org.junit.Assert.assertThrows;
+
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.ServiceSpecificException;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.LargeTest;
@@ -114,6 +117,34 @@
}
}
+ /**
+ * Test that exceptions created when parcelling data in the service are really
+ * sent to the client and re-thrown.
+ */
+ public void testThrownException() throws Exception {
+ final List<ThrowingObject> throwers = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ throwers.add(new ThrowingObject(/* throws= */ false));
+ }
+ throwers.add(new ThrowingObject(/* throws= */ true));
+
+ final ParceledListSlice<ThrowingObject> src = new ParceledListSlice<>(throwers);
+ src.setInlineCountLimit(1);
+
+ Parcel parcel = Parcel.obtain();
+ try {
+ parcel.writeParcelable(src, 0);
+ parcel.setDataPosition(0);
+
+ assertThrows(ServiceSpecificException.class, () -> {
+ final ParceledListSlice<ThrowingObject> dst =
+ parcel.readParcelable(getClass().getClassLoader());
+ });
+ } finally {
+ parcel.recycle();
+ }
+ }
+
private void sendParcelStringList(List<String> list) {
StringParceledListSlice slice;
Parcel parcel = Parcel.obtain();
@@ -236,6 +267,40 @@
};
}
+ public static class ThrowingObject implements Parcelable {
+
+ private final boolean mShouldThrow;
+
+ public ThrowingObject(boolean shouldThrow) {
+ mShouldThrow = shouldThrow;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (mShouldThrow) {
+ throw new ServiceSpecificException(1234);
+ }
+ dest.writeBoolean(mShouldThrow);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<ThrowingObject> CREATOR = new Creator<ThrowingObject>() {
+ @Override
+ public ThrowingObject createFromParcel(Parcel source) {
+ return new ThrowingObject(source.readBoolean());
+ }
+
+ @Override
+ public ThrowingObject[] newArray(int size) {
+ return new ThrowingObject[size];
+ }
+ };
+ }
+
public static class SmallObject extends BaseObject {
public int mFieldA;
public int mFieldB;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 129924a..f77ac81 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -143,8 +143,8 @@
private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
return taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
|| (taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
- && taskInfo.configuration.windowConfiguration.getDisplayWindowingMode()
- == WINDOWING_MODE_FREEFORM);
+ && taskInfo.configuration.windowConfiguration.getDisplayWindowingMode()
+ == WINDOWING_MODE_FREEFORM);
}
private void createWindowDecoration(
@@ -175,16 +175,18 @@
new CaptionTouchEventListener(taskInfo, taskPositioner);
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
windowDecoration.setDragResizeCallback(taskPositioner);
+ windowDecoration.setDragDetector(touchEventListener.mDragDetector);
windowDecoration.relayout(taskInfo, startT, finishT);
setupCaptionColor(taskInfo, windowDecoration);
}
private class CaptionTouchEventListener implements
- View.OnClickListener, View.OnTouchListener {
+ View.OnClickListener, View.OnTouchListener, DragDetector.MotionEventHandler {
private final int mTaskId;
private final WindowContainerToken mTaskToken;
private final DragResizeCallback mDragResizeCallback;
+ private final DragDetector mDragDetector;
private int mDragPointerId = -1;
@@ -194,6 +196,7 @@
mTaskId = taskInfo.taskId;
mTaskToken = taskInfo.token;
mDragResizeCallback = dragResizeCallback;
+ mDragDetector = new DragDetector(this);
}
@Override
@@ -216,7 +219,7 @@
if (v.getId() != R.id.caption) {
return false;
}
- handleEventForMove(e);
+ mDragDetector.onMotionEvent(e);
if (e.getAction() != MotionEvent.ACTION_DOWN) {
return false;
@@ -235,10 +238,11 @@
* @param e {@link MotionEvent} to process
* @return {@code true} if a drag is happening; or {@code false} if it is not
*/
- private void handleEventForMove(MotionEvent e) {
+ @Override
+ public boolean handleMotionEvent(MotionEvent e) {
final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
- return;
+ return false;
}
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
@@ -261,6 +265,7 @@
break;
}
}
+ return true;
}
}
-}
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index d26f1fc..f94fbfc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -49,7 +49,7 @@
private View.OnTouchListener mOnCaptionTouchListener;
private DragResizeCallback mDragResizeCallback;
private DragResizeInputListener mDragResizeListener;
- private final DragDetector mDragDetector;
+ private DragDetector mDragDetector;
private RelayoutParams mRelayoutParams = new RelayoutParams();
private final RelayoutResult<WindowDecorLinearLayout> mResult =
@@ -69,7 +69,6 @@
mHandler = handler;
mChoreographer = choreographer;
mSyncQueue = syncQueue;
- mDragDetector = new DragDetector(ViewConfiguration.get(context).getScaledTouchSlop());
}
void setCaptionListeners(
@@ -83,6 +82,11 @@
mDragResizeCallback = dragResizeCallback;
}
+ void setDragDetector(DragDetector dragDetector) {
+ mDragDetector = dragDetector;
+ mDragDetector.setTouchSlop(ViewConfiguration.get(mContext).getScaledTouchSlop());
+ }
+
@Override
void relayout(RunningTaskInfo taskInfo) {
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 2863adc..bd3afa9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -205,7 +205,7 @@
}
private class DesktopModeTouchEventListener implements
- View.OnClickListener, View.OnTouchListener {
+ View.OnClickListener, View.OnTouchListener, DragDetector.MotionEventHandler {
private final int mTaskId;
private final WindowContainerToken mTaskToken;
@@ -216,12 +216,11 @@
private DesktopModeTouchEventListener(
RunningTaskInfo taskInfo,
- DragResizeCallback dragResizeCallback,
- DragDetector dragDetector) {
+ DragResizeCallback dragResizeCallback) {
mTaskId = taskInfo.taskId;
mTaskToken = taskInfo.token;
mDragResizeCallback = dragResizeCallback;
- mDragDetector = dragDetector;
+ mDragDetector = new DragDetector(this);
}
@Override
@@ -254,8 +253,7 @@
return false;
}
if (id == R.id.caption_handle) {
- isDrag = mDragDetector.detectDragEvent(e);
- handleEventForMove(e);
+ isDrag = mDragDetector.onMotionEvent(e);
}
if (e.getAction() != MotionEvent.ACTION_DOWN) {
return isDrag;
@@ -272,19 +270,19 @@
/**
* @param e {@link MotionEvent} to process
- * @return {@code true} if a drag is happening; or {@code false} if it is not
+ * @return {@code true} if the motion event is handled.
*/
- private void handleEventForMove(MotionEvent e) {
+ @Override
+ public boolean handleMotionEvent(MotionEvent e) {
final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
if (DesktopModeStatus.isProto2Enabled()
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
- return;
+ return false;
}
if (DesktopModeStatus.isProto1Enabled() && mDesktopModeController.isPresent()
- && mDesktopModeController.get().getDisplayAreaWindowingMode(
- taskInfo.displayId)
+ && mDesktopModeController.get().getDisplayAreaWindowingMode(taskInfo.displayId)
== WINDOWING_MODE_FULLSCREEN) {
- return;
+ return false;
}
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
@@ -324,6 +322,7 @@
break;
}
}
+ return true;
}
}
@@ -560,10 +559,10 @@
final TaskPositioner taskPositioner =
new TaskPositioner(mTaskOrganizer, windowDecoration, mDragStartListener);
final DesktopModeTouchEventListener touchEventListener =
- new DesktopModeTouchEventListener(
- taskInfo, taskPositioner, windowDecoration.getDragDetector());
+ new DesktopModeTouchEventListener(taskInfo, taskPositioner);
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
windowDecoration.setDragResizeCallback(taskPositioner);
+ windowDecoration.setDragDetector(touchEventListener.mDragDetector);
windowDecoration.relayout(taskInfo, startT, finishT);
incrementEventReceiverTasks(taskInfo.displayId);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 1a38d24..9550937 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -57,7 +57,7 @@
private View.OnTouchListener mOnCaptionTouchListener;
private DragResizeCallback mDragResizeCallback;
private DragResizeInputListener mDragResizeListener;
- private final DragDetector mDragDetector;
+ private DragDetector mDragDetector;
private RelayoutParams mRelayoutParams = new RelayoutParams();
private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult =
@@ -81,7 +81,6 @@
mChoreographer = choreographer;
mSyncQueue = syncQueue;
mDesktopActive = DesktopModeStatus.isActive(mContext);
- mDragDetector = new DragDetector(ViewConfiguration.get(context).getScaledTouchSlop());
}
void setCaptionListeners(
@@ -95,8 +94,9 @@
mDragResizeCallback = dragResizeCallback;
}
- DragDetector getDragDetector() {
- return mDragDetector;
+ void setDragDetector(DragDetector dragDetector) {
+ mDragDetector = dragDetector;
+ mDragDetector.setTouchSlop(ViewConfiguration.get(mContext).getScaledTouchSlop());
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
index 0abe8ab..4fac843 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.windowdecor;
+import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
@@ -25,63 +26,82 @@
import android.view.MotionEvent;
/**
- * A detector for touch inputs that differentiates between drag and click inputs.
+ * A detector for touch inputs that differentiates between drag and click inputs. It receives a flow
+ * of {@link MotionEvent} and generates a new flow of motion events with slop in consideration to
+ * the event handler. In particular, it always passes down, up and cancel events. It'll pass move
+ * events only when there is at least one move event that's beyond the slop threshold. For the
+ * purpose of convenience it also passes all events of other actions.
+ *
* All touch events must be passed through this class to track a drag event.
*/
-public class DragDetector {
+class DragDetector {
+ private final MotionEventHandler mEventHandler;
+
+ private final PointF mInputDownPoint = new PointF();
private int mTouchSlop;
- private PointF mInputDownPoint;
private boolean mIsDragEvent;
private int mDragPointerId;
- public DragDetector(int touchSlop) {
- mTouchSlop = touchSlop;
- mInputDownPoint = new PointF();
- mIsDragEvent = false;
- mDragPointerId = -1;
+
+ private boolean mResultOfDownAction;
+
+ DragDetector(MotionEventHandler eventHandler) {
+ resetState();
+ mEventHandler = eventHandler;
}
/**
- * Determine if {@link MotionEvent} is part of a drag event.
- * @return {@code true} if this is a drag event, {@code false} if not
- */
- public boolean detectDragEvent(MotionEvent ev) {
- switch (ev.getAction()) {
+ * The receiver of the {@link MotionEvent} flow.
+ *
+ * @return the result returned by {@link #mEventHandler}, or the result when
+ * {@link #mEventHandler} handles the previous down event if the event shouldn't be passed
+ */
+ boolean onMotionEvent(MotionEvent ev) {
+ switch (ev.getActionMasked()) {
case ACTION_DOWN: {
+ // Only touch screens generate noisy moves.
+ mIsDragEvent = (ev.getSource() & SOURCE_TOUCHSCREEN) != SOURCE_TOUCHSCREEN;
mDragPointerId = ev.getPointerId(0);
float rawX = ev.getRawX(0);
float rawY = ev.getRawY(0);
mInputDownPoint.set(rawX, rawY);
- return false;
+ mResultOfDownAction = mEventHandler.handleMotionEvent(ev);
+ return mResultOfDownAction;
}
case ACTION_MOVE: {
if (!mIsDragEvent) {
int dragPointerIndex = ev.findPointerIndex(mDragPointerId);
float dx = ev.getRawX(dragPointerIndex) - mInputDownPoint.x;
float dy = ev.getRawY(dragPointerIndex) - mInputDownPoint.y;
- if (Math.hypot(dx, dy) > mTouchSlop) {
- mIsDragEvent = true;
- }
+ mIsDragEvent = Math.hypot(dx, dy) > mTouchSlop;
}
- return mIsDragEvent;
+ if (mIsDragEvent) {
+ return mEventHandler.handleMotionEvent(ev);
+ } else {
+ return mResultOfDownAction;
+ }
}
- case ACTION_UP: {
- boolean result = mIsDragEvent;
- mIsDragEvent = false;
- mInputDownPoint.set(0, 0);
- mDragPointerId = -1;
- return result;
- }
+ case ACTION_UP:
case ACTION_CANCEL: {
- mIsDragEvent = false;
- mInputDownPoint.set(0, 0);
- mDragPointerId = -1;
- return false;
+ resetState();
+ return mEventHandler.handleMotionEvent(ev);
}
+ default:
+ return mEventHandler.handleMotionEvent(ev);
}
- return mIsDragEvent;
}
- public void setTouchSlop(int touchSlop) {
+ void setTouchSlop(int touchSlop) {
mTouchSlop = touchSlop;
}
+
+ private void resetState() {
+ mIsDragEvent = false;
+ mInputDownPoint.set(0, 0);
+ mDragPointerId = -1;
+ mResultOfDownAction = false;
+ }
+
+ interface MotionEventHandler {
+ boolean handleMotionEvent(MotionEvent ev);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index bb67145..ea417b1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -48,7 +48,6 @@
* Task edges are for resizing with a mouse.
* Task corners are for resizing with touch input.
*/
-// TODO(b/251270585): investigate how to pass taps in corners to the tasks
class DragResizeInputListener implements AutoCloseable {
private static final String TAG = "DragResizeInputListener";
@@ -116,7 +115,8 @@
mInputEventReceiver = new TaskResizeInputEventReceiver(
mInputChannel, mHandler, mChoreographer);
mCallback = callback;
- mDragDetector = new DragDetector(ViewConfiguration.get(context).getScaledTouchSlop());
+ mDragDetector = new DragDetector(mInputEventReceiver);
+ mDragDetector.setTouchSlop(ViewConfiguration.get(context).getScaledTouchSlop());
}
/**
@@ -224,12 +224,12 @@
}
}
- private class TaskResizeInputEventReceiver extends InputEventReceiver {
+ private class TaskResizeInputEventReceiver extends InputEventReceiver
+ implements DragDetector.MotionEventHandler {
private final Choreographer mChoreographer;
private final Runnable mConsumeBatchEventRunnable;
private boolean mConsumeBatchEventScheduled;
private boolean mShouldHandleEvents;
- private boolean mDragging;
private TaskResizeInputEventReceiver(
InputChannel inputChannel, Handler handler, Choreographer choreographer) {
@@ -271,15 +271,15 @@
if (!(inputEvent instanceof MotionEvent)) {
return false;
}
+ return mDragDetector.onMotionEvent((MotionEvent) inputEvent);
+ }
- MotionEvent e = (MotionEvent) inputEvent;
+ @Override
+ public boolean handleMotionEvent(MotionEvent e) {
boolean result = false;
// Check if this is a touch event vs mouse event.
// Touch events are tracked in four corners. Other events are tracked in resize edges.
boolean isTouch = (e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
- if (isTouch) {
- mDragging = mDragDetector.detectDragEvent(e);
- }
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
float x = e.getX(0);
@@ -306,24 +306,17 @@
int dragPointerIndex = e.findPointerIndex(mDragPointerId);
float rawX = e.getRawX(dragPointerIndex);
float rawY = e.getRawY(dragPointerIndex);
- if (!isTouch) {
- // For all other types allow immediate dragging.
- mDragging = true;
- }
- if (mDragging) {
- mCallback.onDragResizeMove(rawX, rawY);
- result = true;
- }
+ mCallback.onDragResizeMove(rawX, rawY);
+ result = true;
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
- if (mShouldHandleEvents && mDragging) {
+ if (mShouldHandleEvents) {
int dragPointerIndex = e.findPointerIndex(mDragPointerId);
mCallback.onDragResizeEnd(
e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex));
}
- mDragging = false;
mShouldHandleEvents = false;
mDragPointerId = -1;
result = true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
index 20631f8..8cd2a59 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
@@ -40,9 +40,7 @@
private final Rect mTaskBoundsAtDragStart = new Rect();
private final PointF mResizeStartPoint = new PointF();
private final Rect mResizeTaskBounds = new Rect();
- // Whether the |dragResizing| hint should be sent with the next bounds change WCT.
- // Used to optimized fluid resizing of freeform tasks.
- private boolean mPendingDragResizeHint = false;
+ private boolean mHasMoved = false;
private int mCtrlType;
private DragStartListener mDragStartListener;
@@ -60,11 +58,7 @@
@Override
public void onDragResizeStart(int ctrlType, float x, float y) {
- if (ctrlType != CTRL_TYPE_UNDEFINED) {
- // The task is being resized, send the |dragResizing| hint to core with the first
- // bounds-change wct.
- mPendingDragResizeHint = true;
- }
+ mHasMoved = false;
mDragStartListener.onDragStart(mWindowDecoration.mTaskInfo.taskId);
mCtrlType = ctrlType;
@@ -78,30 +72,44 @@
public void onDragResizeMove(float x, float y) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (changeBounds(wct, x, y)) {
- if (mPendingDragResizeHint) {
+ // The task is being resized, send the |dragResizing| hint to core with the first
+ // bounds-change wct.
+ if (!mHasMoved && mCtrlType != CTRL_TYPE_UNDEFINED) {
// This is the first bounds change since drag resize operation started.
wct.setDragResizing(mWindowDecoration.mTaskInfo.token, true /* dragResizing */);
- mPendingDragResizeHint = false;
}
mTaskOrganizer.applyTransaction(wct);
+ mHasMoved = true;
}
}
@Override
public void onDragResizeEnd(float x, float y) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setDragResizing(mWindowDecoration.mTaskInfo.token, false /* dragResizing */);
- changeBounds(wct, x, y);
- mTaskOrganizer.applyTransaction(wct);
+ // |mHasMoved| being false means there is no real change to the task bounds in WM core, so
+ // we don't need a WCT to finish it.
+ if (mHasMoved) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setDragResizing(mWindowDecoration.mTaskInfo.token, false /* dragResizing */);
+ changeBounds(wct, x, y);
+ mTaskOrganizer.applyTransaction(wct);
+ }
- mCtrlType = 0;
+ mCtrlType = CTRL_TYPE_UNDEFINED;
mTaskBoundsAtDragStart.setEmpty();
mResizeStartPoint.set(0, 0);
- mPendingDragResizeHint = false;
+ mHasMoved = false;
}
private boolean changeBounds(WindowContainerTransaction wct, float x, float y) {
- float deltaX = x - mResizeStartPoint.x;
+ // |mResizeTaskBounds| is the bounds last reported if |mHasMoved| is true. If it's not true,
+ // we can compare it against |mTaskBoundsAtDragStart|.
+ final int oldLeft = mHasMoved ? mResizeTaskBounds.left : mTaskBoundsAtDragStart.left;
+ final int oldTop = mHasMoved ? mResizeTaskBounds.top : mTaskBoundsAtDragStart.top;
+ final int oldRight = mHasMoved ? mResizeTaskBounds.right : mTaskBoundsAtDragStart.right;
+ final int oldBottom = mHasMoved ? mResizeTaskBounds.bottom : mTaskBoundsAtDragStart.bottom;
+
+ final float deltaX = x - mResizeStartPoint.x;
+ final float deltaY = y - mResizeStartPoint.y;
mResizeTaskBounds.set(mTaskBoundsAtDragStart);
if ((mCtrlType & CTRL_TYPE_LEFT) != 0) {
mResizeTaskBounds.left += deltaX;
@@ -109,22 +117,22 @@
if ((mCtrlType & CTRL_TYPE_RIGHT) != 0) {
mResizeTaskBounds.right += deltaX;
}
- float deltaY = y - mResizeStartPoint.y;
if ((mCtrlType & CTRL_TYPE_TOP) != 0) {
mResizeTaskBounds.top += deltaY;
}
if ((mCtrlType & CTRL_TYPE_BOTTOM) != 0) {
mResizeTaskBounds.bottom += deltaY;
}
- if (mCtrlType == 0) {
+ if (mCtrlType == CTRL_TYPE_UNDEFINED) {
mResizeTaskBounds.offset((int) deltaX, (int) deltaY);
}
- if (!mResizeTaskBounds.isEmpty()) {
- wct.setBounds(mWindowDecoration.mTaskInfo.token, mResizeTaskBounds);
- return true;
+ if (oldLeft == mResizeTaskBounds.left && oldTop == mResizeTaskBounds.top
+ && oldRight == mResizeTaskBounds.right && oldBottom == mResizeTaskBounds.bottom) {
+ return false;
}
- return false;
+ wct.setBounds(mWindowDecoration.mTaskInfo.token, mResizeTaskBounds);
+ return true;
}
interface DragStartListener {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
index 85812c4..8c3bea8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
@@ -23,6 +23,7 @@
import com.android.server.wm.flicker.FlickerBuilder
import com.android.server.wm.flicker.FlickerTest
import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import com.android.server.wm.traces.common.service.PlatformConsts
import com.android.wm.shell.flicker.appWindowBecomesVisible
@@ -30,6 +31,7 @@
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
import com.android.wm.shell.flicker.splitScreenEntered
+import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -71,7 +73,19 @@
@Test
fun splitScreenDividerBecomesVisible() = flicker.splitScreenDividerBecomesVisible()
- @Presubmit @Test fun primaryAppLayerBecomesVisible() = flicker.layerBecomesVisible(primaryApp)
+ @FlakyTest
+ @Test
+ fun primaryAppLayerBecomesVisible() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ flicker.layerBecomesVisible(primaryApp)
+ }
+
+ @Presubmit
+ @Test
+ fun primaryAppLayerBecomesVisibleShellTransit() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ flicker.layerBecomesVisible(primaryApp)
+ }
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
index 7c62433..06a1449 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
@@ -23,6 +23,7 @@
import com.android.server.wm.flicker.FlickerBuilder
import com.android.server.wm.flicker.FlickerTest
import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
import com.android.server.wm.traces.common.service.PlatformConsts
import com.android.wm.shell.flicker.appWindowBecomesVisible
@@ -30,6 +31,7 @@
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
import com.android.wm.shell.flicker.splitScreenEntered
+import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -71,7 +73,19 @@
@Test
fun splitScreenDividerBecomesVisible() = flicker.splitScreenDividerBecomesVisible()
- @Presubmit @Test fun primaryAppLayerBecomesVisible() = flicker.layerBecomesVisible(primaryApp)
+ @FlakyTest
+ @Test
+ fun primaryAppLayerBecomesVisible() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ flicker.layerBecomesVisible(primaryApp)
+ }
+
+ @Presubmit
+ @Test
+ fun primaryAppLayerBecomesVisibleShellTransit() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ flicker.layerBecomesVisible(primaryApp)
+ }
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt
new file mode 100644
index 0000000..8f84008
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.windowdecor
+
+import android.os.SystemClock
+import android.testing.AndroidTestingRunner
+import android.view.MotionEvent
+import android.view.InputDevice
+import androidx.test.filters.SmallTest
+import org.junit.After
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.any
+import org.mockito.Mockito.argThat
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+/**
+ * Tests for [DragDetector].
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:DragDetectorTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DragDetectorTest {
+ private val motionEvents = mutableListOf<MotionEvent>()
+
+ @Mock
+ private lateinit var eventHandler: DragDetector.MotionEventHandler
+
+ private lateinit var dragDetector: DragDetector
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ `when`(eventHandler.handleMotionEvent(any())).thenReturn(true)
+
+ dragDetector = DragDetector(eventHandler)
+ dragDetector.setTouchSlop(SLOP)
+ }
+
+ @After
+ fun tearDown() {
+ motionEvents.forEach {
+ it.recycle()
+ }
+ motionEvents.clear()
+ }
+
+ @Test
+ fun testNoMove_passesDownAndUp() {
+ assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+ it.source == InputDevice.SOURCE_TOUCHSCREEN
+ })
+
+ assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_UP && it.x == X && it.y == Y &&
+ it.source == InputDevice.SOURCE_TOUCHSCREEN
+ })
+ }
+
+ @Test
+ fun testMoveInSlop_touch_passesDownAndUp() {
+ `when`(eventHandler.handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_DOWN
+ })).thenReturn(false)
+
+ assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+ it.source == InputDevice.SOURCE_TOUCHSCREEN
+ })
+
+ val newX = X + SLOP - 1
+ assertFalse(
+ dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y)))
+ verify(eventHandler, never()).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_MOVE
+ })
+
+ assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP, newX, Y)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y &&
+ it.source == InputDevice.SOURCE_TOUCHSCREEN
+ })
+ }
+
+ @Test
+ fun testMoveInSlop_mouse_passesDownMoveAndUp() {
+ `when`(eventHandler.handleMotionEvent(argThat {
+ it.action == MotionEvent.ACTION_DOWN
+ })).thenReturn(false)
+
+ assertFalse(dragDetector.onMotionEvent(
+ createMotionEvent(MotionEvent.ACTION_DOWN, isTouch = false)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+ it.source == InputDevice.SOURCE_MOUSE
+ })
+
+ val newX = X + SLOP - 1
+ assertTrue(dragDetector.onMotionEvent(
+ createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y, isTouch = false)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
+ it.source == InputDevice.SOURCE_MOUSE
+ })
+
+ assertTrue(dragDetector.onMotionEvent(
+ createMotionEvent(MotionEvent.ACTION_UP, newX, Y, isTouch = false)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y &&
+ it.source == InputDevice.SOURCE_MOUSE
+ })
+ }
+
+ @Test
+ fun testMoveBeyondSlop_passesDownMoveAndUp() {
+ `when`(eventHandler.handleMotionEvent(argThat {
+ it.action == MotionEvent.ACTION_DOWN
+ })).thenReturn(false)
+
+ assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+ it.source == InputDevice.SOURCE_TOUCHSCREEN
+ })
+
+ val newX = X + SLOP + 1
+ assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
+ it.source == InputDevice.SOURCE_TOUCHSCREEN
+ })
+
+ assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP, newX, Y)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y &&
+ it.source == InputDevice.SOURCE_TOUCHSCREEN
+ })
+ }
+
+ @Test
+ fun testPassesHoverEnter() {
+ `when`(eventHandler.handleMotionEvent(argThat {
+ it.action == MotionEvent.ACTION_HOVER_ENTER
+ })).thenReturn(false)
+
+ assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_ENTER)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_HOVER_ENTER && it.x == X && it.y == Y
+ })
+ }
+
+ @Test
+ fun testPassesHoverMove() {
+ assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_MOVE)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_HOVER_MOVE && it.x == X && it.y == Y
+ })
+ }
+
+ @Test
+ fun testPassesHoverExit() {
+ assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_EXIT)))
+ verify(eventHandler).handleMotionEvent(argThat {
+ return@argThat it.action == MotionEvent.ACTION_HOVER_EXIT && it.x == X && it.y == Y
+ })
+ }
+
+ private fun createMotionEvent(action: Int, x: Float = X, y: Float = Y, isTouch: Boolean = true):
+ MotionEvent {
+ val time = SystemClock.uptimeMillis()
+ val ev = MotionEvent.obtain(time, time, action, x, y, 0)
+ ev.source = if (isTouch) InputDevice.SOURCE_TOUCHSCREEN else InputDevice.SOURCE_MOUSE
+ motionEvents.add(ev)
+ return ev
+ }
+
+ companion object {
+ private const val SLOP = 10
+ private const val X = 123f
+ private const val Y = 234f
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
index ac10ddb..804c416 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
@@ -1,6 +1,7 @@
package com.android.wm.shell.windowdecor
import android.app.ActivityManager
+import android.app.WindowConfiguration
import android.graphics.Rect
import android.os.IBinder
import android.testing.AndroidTestingRunner
@@ -10,6 +11,7 @@
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_RIGHT
+import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_TOP
import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_UNDEFINED
import org.junit.Before
import org.junit.Test
@@ -63,6 +65,90 @@
}
@Test
+ fun testDragResize_notMove_skipsTransactionOnEnd() {
+ taskPositioner.onDragResizeStart(
+ CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat()
+ )
+
+ taskPositioner.onDragResizeEnd(
+ STARTING_BOUNDS.left.toFloat() + 10,
+ STARTING_BOUNDS.top.toFloat() + 10
+ )
+
+ verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)
+ }
+ })
+ }
+
+ @Test
+ fun testDragResize_noEffectiveMove_skipsTransactionOnMoveAndEnd() {
+ taskPositioner.onDragResizeStart(
+ CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat()
+ )
+
+ taskPositioner.onDragResizeMove(
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat()
+ )
+
+ taskPositioner.onDragResizeEnd(
+ STARTING_BOUNDS.left.toFloat() + 10,
+ STARTING_BOUNDS.top.toFloat() + 10
+ )
+
+ verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)
+ }
+ })
+ }
+
+ @Test
+ fun testDragResize_hasEffectiveMove_issuesTransactionOnMoveAndEnd() {
+ taskPositioner.onDragResizeStart(
+ CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat()
+ )
+
+ taskPositioner.onDragResizeMove(
+ STARTING_BOUNDS.left.toFloat() + 10,
+ STARTING_BOUNDS.top.toFloat()
+ )
+ val rectAfterMove = Rect(STARTING_BOUNDS)
+ rectAfterMove.right += 10
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+ change.configuration.windowConfiguration.bounds == rectAfterMove
+ }
+ })
+
+ taskPositioner.onDragResizeEnd(
+ STARTING_BOUNDS.left.toFloat() + 10,
+ STARTING_BOUNDS.top.toFloat() + 10
+ )
+ val rectAfterEnd = Rect(rectAfterMove)
+ rectAfterEnd.top += 10
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+ change.configuration.windowConfiguration.bounds == rectAfterEnd
+ }
+ })
+ }
+
+ @Test
fun testDragResize_move_skipsDragResizingFlag() {
taskPositioner.onDragResizeStart(
CTRL_TYPE_UNDEFINED, // Move
diff --git a/libs/securebox/Android.bp b/libs/securebox/Android.bp
new file mode 100644
index 0000000..a29c03c
--- /dev/null
+++ b/libs/securebox/Android.bp
@@ -0,0 +1,8 @@
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_library {
+ name: "securebox",
+ srcs: ["src/**/*.java"],
+}
diff --git a/libs/securebox/OWNERS b/libs/securebox/OWNERS
new file mode 100644
index 0000000..e160799
--- /dev/null
+++ b/libs/securebox/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/locksettings/recoverablekeystore/OWNERS
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/SecureBox.java b/libs/securebox/src/com/android/security/SecureBox.java
similarity index 98%
rename from services/core/java/com/android/server/locksettings/recoverablekeystore/SecureBox.java
rename to libs/securebox/src/com/android/security/SecureBox.java
index 51a37b3..0ebaff4 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/SecureBox.java
+++ b/libs/securebox/src/com/android/security/SecureBox.java
@@ -14,11 +14,13 @@
* limitations under the License.
*/
-package com.android.server.locksettings.recoverablekeystore;
+package com.android.security;
import android.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+
import java.math.BigInteger;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
@@ -41,6 +43,7 @@
import java.security.spec.EllipticCurve;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
+
import javax.crypto.AEADBadTagException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
@@ -380,7 +383,7 @@
* @param publicKey The public key.
* @return The key packed into a 65-byte array.
*/
- static byte[] encodePublicKey(PublicKey publicKey) {
+ public static byte[] encodePublicKey(PublicKey publicKey) {
ECPoint point = ((ECPublicKey) publicKey).getW();
byte[] x = point.getAffineX().toByteArray();
byte[] y = point.getAffineY().toByteArray();
@@ -394,8 +397,13 @@
return output;
}
- @VisibleForTesting
- static PublicKey decodePublicKey(byte[] keyBytes)
+ /**
+ * Decodes byte[] encoded public key.
+ *
+ * @param keyBytes encoded public key
+ * @return the public key
+ */
+ public static PublicKey decodePublicKey(byte[] keyBytes)
throws NoSuchAlgorithmException, InvalidKeyException {
BigInteger x =
new BigInteger(
diff --git a/libs/securebox/tests/Android.bp b/libs/securebox/tests/Android.bp
new file mode 100644
index 0000000..7df546a
--- /dev/null
+++ b/libs/securebox/tests/Android.bp
@@ -0,0 +1,46 @@
+// 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 {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "SecureBoxTests",
+ srcs: [
+ "**/*.java",
+ ],
+ static_libs: [
+ "securebox",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "frameworks-base-testutils",
+ "junit",
+ "mockito-target-extended-minus-junit4",
+ "platform-test-annotations",
+ "testables",
+ "testng",
+ "truth-prebuilt",
+ ],
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ "android.test.runner",
+ ],
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
+}
diff --git a/libs/securebox/tests/AndroidManifest.xml b/libs/securebox/tests/AndroidManifest.xml
new file mode 100644
index 0000000..3dc9563
--- /dev/null
+++ b/libs/securebox/tests/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.security.tests">
+
+ <application android:debuggable="true" android:largeHeap="true">
+ <uses-library android:name="android.test.mock" />
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:label="Tests for SecureBox"
+ android:targetPackage="com.android.security.tests">
+ </instrumentation>
+
+</manifest>
diff --git a/libs/securebox/tests/AndroidTest.xml b/libs/securebox/tests/AndroidTest.xml
new file mode 100644
index 0000000..54abd135
--- /dev/null
+++ b/libs/securebox/tests/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+<configuration description="Runs Tests for SecureBox">
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="install-arg" value="-t" />
+ <option name="test-file-name" value="SecureBoxTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="framework-base-presubmit" />
+ <option name="test-tag" value="SecureBoxTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.security.tests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/SecureBoxTest.java b/libs/securebox/tests/src/com/android/security/SecureBoxTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/SecureBoxTest.java
rename to libs/securebox/tests/src/com/android/security/SecureBoxTest.java
index 34235bd..b6e2365 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/SecureBoxTest.java
+++ b/libs/securebox/tests/src/com/android/security/SecureBoxTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.locksettings.recoverablekeystore;
+package com.android.security;
import static com.google.common.truth.Truth.assertThat;
@@ -24,11 +24,11 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.util.ArrayUtils;
+
import org.junit.Test;
import org.junit.runner.RunWith;
-import com.android.internal.util.ArrayUtils;
-
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
diff --git a/location/java/android/location/Address.java b/location/java/android/location/Address.java
index bb97c78..90d9929 100644
--- a/location/java/android/location/Address.java
+++ b/location/java/android/location/Address.java
@@ -26,7 +26,7 @@
import android.os.Parcelable;
/**
- * A class representing an Address, i.e, a set of Strings describing a location.
+ * A class representing an Address, that is, a set of Strings describing a location.
*
* The address format is a simplified version of xAL (eXtensible Address Language)
* http://www.oasis-open.org/committees/ciq/ciq.html#6
diff --git a/location/java/android/location/GnssCapabilities.java b/location/java/android/location/GnssCapabilities.java
index f9f9fa4..11b5833 100644
--- a/location/java/android/location/GnssCapabilities.java
+++ b/location/java/android/location/GnssCapabilities.java
@@ -240,7 +240,11 @@
}
/**
- * Returns {@code true} if GNSS chipset supports on demand time, {@code false} otherwise.
+ * Returns {@code true} if GNSS chipset requests periodic time signal injection from the
+ * platform in addition to on-demand and occasional time updates, {@code false} otherwise.
+ *
+ * <p><em>Note: The naming of this capability and the behavior it controls differ substantially.
+ * This is the result of a historic implementation bug, b/73893222.</em>
*/
public boolean hasOnDemandTime() {
return (mTopFlags & TOP_HAL_CAPABILITY_ON_DEMAND_TIME) != 0;
diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java
index 44bb56e..7ac8446 100644
--- a/media/java/android/media/ImageUtils.java
+++ b/media/java/android/media/ImageUtils.java
@@ -85,6 +85,7 @@
public static int getNumPlanesForHardwareBufferFormat(int hardwareBufferFormat) {
switch(hardwareBufferFormat) {
case HardwareBuffer.YCBCR_420_888:
+ case HardwareBuffer.YCBCR_P010:
return 3;
case HardwareBuffer.RGBA_8888:
case HardwareBuffer.RGBX_8888:
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 0c97989..2779fa2 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -57,6 +57,7 @@
<uses-permission android:name="android.permission.ACCEPT_HANDOVER" />
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.permission.BODY_SENSORS" />
+ <uses-permission android:name="android.permission.BODY_SENSORS_WRIST_TEMPERATURE" />
<uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
<uses-permission android:name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
<uses-permission android:name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" />
@@ -511,6 +512,9 @@
<!-- Permission needed for CTS test - DefaultDisplayModeTest -->
<uses-permission android:name="android.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE" />
+ <!-- Permission needed for CTS test - HdrConversionTest -->
+ <uses-permission android:name="android.permission.MODIFY_HDR_CONVERSION_MODE" />
+
<!-- Permissions needed for manual testing telephony time zone detector behavior -->
<uses-permission android:name="android.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE" />
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_down_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_down_24dp.xml
new file mode 100644
index 0000000..a64a0d1
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_down_24dp.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="@color/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,8.69L20,4h-4.69L12,0.69 8.69,4L4,4v4.69L0.69,12 4,15.31L4,20h4.69L12,23.31 15.31,20L20,20v-4.69L23.31,12 20,8.69zM18,14.48L18,18h-3.52L12,20.48 9.52,18L6,18v-3.52L3.52,12 6,9.52L6,6h3.52L12,3.52 14.48,6L18,6v3.52L20.48,12 18,14.48zM12,9c1.65,0 3,1.35 3,3s-1.35,3 -3,3 -3,-1.35 -3,-3 1.35,-3 3,-3m0,-2c-2.76,0 -5,2.24 -5,5s2.24,5 5,5 5,-2.24 5,-5 -2.24,-5 -5,-5z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_up_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_up_24dp.xml
new file mode 100644
index 0000000..40423c7
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_up_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="@color/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,8.69L20,4h-4.69L12,0.69 8.69,4L4,4v4.69L0.69,12 4,15.31L4,20h4.69L12,23.31 15.31,20L20,20v-4.69L23.31,12 20,8.69zM18,14.48L18,18h-3.52L12,20.48 9.52,18L6,18v-3.52L3.52,12 6,9.52L6,6h3.52L12,3.52 14.48,6L18,6v3.52L20.48,12 18,14.48zM12,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5 5,-2.24 5,-5 -2.24,-5 -5,-5z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_lock_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_lock_24dp.xml
new file mode 100644
index 0000000..a0f7b5d
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_lock_24dp.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="@color/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM9,6c0,-1.66 1.34,-3 3,-3s3,1.34 3,3v2L9,8L9,6zM18,20L6,20L6,10h12v10zM12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_notifications_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_notifications_24dp.xml
new file mode 100644
index 0000000..8757f22
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_notifications_24dp.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="@color/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M18,17v-6c0,-3.07 -1.63,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.64,5.36 6,7.92 6,11v6L4,17v2h16v-2h-2zM16,17L8,17v-6c0,-2.48 1.51,-4.5 4,-4.5s4,2.02 4,4.5v6zM12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_power_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_power_24dp.xml
new file mode 100644
index 0000000..049013a
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_power_24dp.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="@color/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M11,2h2v10h-2zM18.37,5.64l-1.41,1.41c2.73,2.73 2.72,7.16 -0.01,9.89 -2.73,2.73 -7.17,2.73 -9.89,0.01 -2.73,-2.73 -2.74,-7.18 -0.01,-9.91l-1.41,-1.4c-3.51,3.51 -3.51,9.21 0.01,12.73 3.51,3.51 9.21,3.51 12.72,-0.01 3.51,-3.51 3.51,-9.2 0,-12.72z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_quick_settings_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_quick_settings_24dp.xml
new file mode 100644
index 0000000..4f25e7d
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_quick_settings_24dp.xml
@@ -0,0 +1,29 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="@color/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M13.85,22.25h-3.7c-0.74,0 -1.36,-0.54 -1.45,-1.27l-0.27,-1.89c-0.27,-0.14 -0.53,-0.29 -0.79,-0.46l-1.8,0.72c-0.7,0.26 -1.47,-0.03 -1.81,-0.65L2.2,15.53c-0.35,-0.66 -0.2,-1.44 0.36,-1.88l1.53,-1.19c-0.01,-0.15 -0.02,-0.3 -0.02,-0.46 0,-0.15 0.01,-0.31 0.02,-0.46l-1.52,-1.19c-0.59,-0.45 -0.74,-1.26 -0.37,-1.88l1.85,-3.19c0.34,-0.62 1.11,-0.9 1.79,-0.63l1.81,0.73c0.26,-0.17 0.52,-0.32 0.78,-0.46l0.27,-1.91c0.09,-0.7 0.71,-1.25 1.44,-1.25h3.7c0.74,0 1.36,0.54 1.45,1.27l0.27,1.89c0.27,0.14 0.53,0.29 0.79,0.46l1.8,-0.72c0.71,-0.26 1.48,0.03 1.82,0.65l1.84,3.18c0.36,0.66 0.2,1.44 -0.36,1.88l-1.52,1.19c0.01,0.15 0.02,0.3 0.02,0.46s-0.01,0.31 -0.02,0.46l1.52,1.19c0.56,0.45 0.72,1.23 0.37,1.86l-1.86,3.22c-0.34,0.62 -1.11,0.9 -1.8,0.63l-1.8,-0.72c-0.26,0.17 -0.52,0.32 -0.78,0.46l-0.27,1.91c-0.1,0.68 -0.72,1.22 -1.46,1.22zM10.62,20.25h2.76l0.37,-2.55 0.53,-0.22c0.44,-0.18 0.88,-0.44 1.34,-0.78l0.45,-0.34 2.38,0.96 1.38,-2.4 -2.03,-1.58 0.07,-0.56c0.03,-0.26 0.06,-0.51 0.06,-0.78s-0.03,-0.53 -0.06,-0.78l-0.07,-0.56 2.03,-1.58 -1.39,-2.4 -2.39,0.96 -0.45,-0.35c-0.42,-0.32 -0.87,-0.58 -1.33,-0.77l-0.52,-0.22 -0.37,-2.55h-2.76l-0.37,2.55 -0.53,0.21c-0.44,0.19 -0.88,0.44 -1.34,0.79l-0.45,0.33 -2.38,-0.95 -1.39,2.39 2.03,1.58 -0.07,0.56c-0.03,0.26 -0.06,0.53 -0.06,0.79s0.02,0.53 0.06,0.78l0.07,0.56 -2.03,1.58 1.38,2.4 2.39,-0.96 0.45,0.35c0.43,0.33 0.86,0.58 1.33,0.77l0.53,0.22 0.38,2.55z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M12,12m-3.5,0a3.5,3.5 0,1 1,7 0a3.5,3.5 0,1 1,-7 0"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_recent_apps_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_recent_apps_24dp.xml
new file mode 100644
index 0000000..38234c0
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_recent_apps_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="@color/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M2,7h4v10H2V7zM7,19h10V5H7V19zM9,7h6v10H9V7zM18,7h4v10h-4V7z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_screenshot_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_screenshot_24dp.xml
new file mode 100644
index 0000000..6d7f49c
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_screenshot_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="@color/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,21L7,21v-1h10v1zM17,18L7,18L7,6h10v12zM17,4L7,4L7,3h10v1zM9.5,8.5L12,8.5L12,7L8,7v4h1.5zM12,17h4v-4h-1.5v2.5L12,15.5z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_settings_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_settings_24dp.xml
new file mode 100644
index 0000000..5ed6f19
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_settings_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="@color/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20.5,4c-2.61,0.7 -5.67,1 -8.5,1s-5.89,-0.3 -8.5,-1L3,6c1.86,0.5 4,0.83 6,1v13h2v-6h2v6h2L15,7c2,-0.17 4.14,-0.5 6,-1l-0.5,-2zM12,4c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM7,24h2v-2L7,22v2zM11,24h2v-2h-2v2zM15,24h2v-2h-2v2z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_down_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_down_24dp.xml
new file mode 100644
index 0000000..16653e8
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_down_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="@color/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M16,7.97v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02 0,-1.77 -1.02,-3.29 -2.5,-4.03zM5,9v6h4l5,5L14,4L9,9L5,9zM12,8.83v6.34L9.83,13L7,13v-2h2.83L12,8.83z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_up_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_up_24dp.xml
new file mode 100644
index 0000000..e572c6a
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_up_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="@color/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM10,8.83v6.34L7.83,13L5,13v-2h2.83L10,8.83zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77 0,-4.28 -2.99,-7.86 -7,-8.77z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml
index 658c03b..91cb4ba 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml
@@ -50,6 +50,17 @@
</LinearLayout>
+ <TextView
+ android:id="@+id/snackbar"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingStart="16dp"
+ android:gravity="center_vertical"
+ android:textColor="@color/colorControlNormal"
+ android:textSize="@dimen/label_text_size"
+ android:background="@color/snackbar_bg_color"/>
+
<View
android:id="@+id/bottom_listDivider"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml
index 33c0cca..a600ec6 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml
@@ -19,5 +19,6 @@
<color name="footer_icon_enabled_color">#E8EAED</color>
<color name="footer_icon_disabled_color">#5F6368</color>
<color name="colorControlNormal">#202124</color>
+ <color name="snackbar_bg_color">@android:color/white</color>
</resources>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml
index 36d1fc1..c1494f9 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml
@@ -20,6 +20,7 @@
<color name="footer_icon_enabled_color">@android:color/black</color>
<color name="footer_icon_disabled_color">#ddd</color>
<color name="colorControlNormal">@android:color/white</color>
+ <color name="snackbar_bg_color">#313235</color>
<color name="colorAccessibilityMenuIcon">#3AA757</color>
</resources>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java
index fa42e61..b6328f0 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java
@@ -15,9 +15,16 @@
*/
package com.android.systemui.accessibility.accessibilitymenu.model;
+import android.util.Log;
+
import com.android.systemui.accessibility.accessibilitymenu.R;
-/** Provides a data structure for a11y menu shortcuts. */
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Provides a data structure for a11y menu shortcuts.
+ */
public class A11yMenuShortcut {
public enum ShortcutId {
@@ -38,6 +45,88 @@
private static final String TAG = "A11yMenuShortcut";
+ // Index of resource ID in the array, shortcutResource.
+ private static final int IMG_SRC_INDEX = 0;
+ private static final int IMG_COLOR_INDEX = 1;
+ private static final int CONTENT_DESCRIPTION_INDEX = 2;
+ private static final int LABEL_TEXT_INDEX = 3;
+
+ /** Map stores all shortcut resource IDs that is in matching order of defined shortcut. */
+ private static final Map<ShortcutId, int[]> sShortcutResource = new HashMap<>() {{
+ put(ShortcutId.ID_ASSISTANT_VALUE, new int[] {
+ R.drawable.ic_logo_assistant_32dp,
+ R.color.assistant_color,
+ R.string.assistant_utterance,
+ R.string.assistant_label,
+ });
+ put(ShortcutId.ID_A11YSETTING_VALUE, new int[] {
+ R.drawable.ic_logo_a11y_settings_24dp,
+ R.color.a11y_settings_color,
+ R.string.a11y_settings_label,
+ R.string.a11y_settings_label,
+ });
+ put(ShortcutId.ID_POWER_VALUE, new int[] {
+ R.drawable.ic_logo_a11y_power_24dp,
+ R.color.power_color,
+ R.string.power_utterance,
+ R.string.power_label,
+ });
+ put(ShortcutId.ID_RECENT_VALUE, new int[] {
+ R.drawable.ic_logo_a11y_recent_apps_24dp,
+ R.color.recent_apps_color,
+ R.string.recent_apps_label,
+ R.string.recent_apps_label,
+ });
+ put(ShortcutId.ID_LOCKSCREEN_VALUE, new int[] {
+ R.drawable.ic_logo_a11y_lock_24dp,
+ R.color.lockscreen_color,
+ R.string.lockscreen_label,
+ R.string.lockscreen_label,
+ });
+ put(ShortcutId.ID_QUICKSETTING_VALUE, new int[] {
+ R.drawable.ic_logo_a11y_quick_settings_24dp,
+ R.color.quick_settings_color,
+ R.string.quick_settings_label,
+ R.string.quick_settings_label,
+ });
+ put(ShortcutId.ID_NOTIFICATION_VALUE, new int[] {
+ R.drawable.ic_logo_a11y_notifications_24dp,
+ R.color.notifications_color,
+ R.string.notifications_label,
+ R.string.notifications_label,
+ });
+ put(ShortcutId.ID_SCREENSHOT_VALUE, new int[] {
+ R.drawable.ic_logo_a11y_screenshot_24dp,
+ R.color.screenshot_color,
+ R.string.screenshot_utterance,
+ R.string.screenshot_label,
+ });
+ put(ShortcutId.ID_BRIGHTNESS_UP_VALUE, new int[] {
+ R.drawable.ic_logo_a11y_brightness_up_24dp,
+ R.color.brightness_color,
+ R.string.brightness_up_label,
+ R.string.brightness_up_label,
+ });
+ put(ShortcutId.ID_BRIGHTNESS_DOWN_VALUE, new int[] {
+ R.drawable.ic_logo_a11y_brightness_down_24dp,
+ R.color.brightness_color,
+ R.string.brightness_down_label,
+ R.string.brightness_down_label,
+ });
+ put(ShortcutId.ID_VOLUME_UP_VALUE, new int[] {
+ R.drawable.ic_logo_a11y_volume_up_24dp,
+ R.color.volume_color,
+ R.string.volume_up_label,
+ R.string.volume_up_label,
+ });
+ put(ShortcutId.ID_VOLUME_DOWN_VALUE, new int[] {
+ R.drawable.ic_logo_a11y_volume_down_24dp,
+ R.color.volume_color,
+ R.string.volume_down_label,
+ R.string.volume_down_label,
+ });
+ }};
+
/** Shortcut id used to identify. */
private int mShortcutId = ShortcutId.UNSPECIFIED_ID_VALUE.ordinal();
@@ -53,17 +142,33 @@
/**
* Sets Id to shortcut, checks the value first and updates shortcut resources. It will set id to
+ * default value {@link ShortcutId.UNSPECIFIED_ID_VALUE} if invalid.
*
* @param id id set to shortcut
*/
public void setId(int id) {
mShortcutId = id;
- // TODO(jonesriley) load the proper resources based on id
- imageSrc = R.drawable.ic_logo_assistant_32dp;
- imageColor = android.R.color.darker_gray;
- imgContentDescription = R.string.empty_content;
- labelText = R.string.empty_content;
+ if (id < ShortcutId.UNSPECIFIED_ID_VALUE.ordinal()
+ || id > ShortcutId.values().length) {
+ mShortcutId = ShortcutId.UNSPECIFIED_ID_VALUE.ordinal();
+ Log.w(
+ TAG, String.format(
+ "setId to default UNSPECIFIED_ID as id is invalid. "
+ + "Max value is %d while id is %d",
+ ShortcutId.values().length, id
+ ));
+ }
+ int[] resources = sShortcutResource.getOrDefault(ShortcutId.values()[id], new int[] {
+ R.drawable.ic_add_32dp,
+ android.R.color.darker_gray,
+ R.string.empty_content,
+ R.string.empty_content,
+ });
+ imageSrc = resources[IMG_SRC_INDEX];
+ imageColor = resources[IMG_COLOR_INDEX];
+ imgContentDescription = resources[CONTENT_DESCRIPTION_INDEX];
+ labelText = resources[LABEL_TEXT_INDEX];
}
public int getId() {
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
index 740bc8a..7bdb8f9 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
@@ -18,10 +18,14 @@
import static java.lang.Math.max;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.content.res.Configuration;
import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
import android.view.Display;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -31,8 +35,11 @@
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowMetrics;
+import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
-import android.widget.Toast;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
import com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService;
import com.android.systemui.accessibility.accessibilitymenu.R;
@@ -69,11 +76,15 @@
private ViewGroup mLayout;
private WindowManager.LayoutParams mLayoutParameter;
private A11yMenuViewPager mA11yMenuViewPager;
+ private Handler mHandler;
+ private AccessibilityManager mAccessibilityManager;
public A11yMenuOverlayLayout(AccessibilityMenuService service) {
mService = service;
mWindowManager = mService.getSystemService(WindowManager.class);
configureLayout();
+ mHandler = new Handler(Looper.getMainLooper());
+ mAccessibilityManager = mService.getSystemService(AccessibilityManager.class);
}
/** Creates Accessibility menu layout and configure layout parameters. */
@@ -261,9 +272,30 @@
mLayout.setVisibility((mLayout.getVisibility() == View.VISIBLE) ? View.GONE : View.VISIBLE);
}
- /** Shows hint text on Toast. */
- public void showToast(String text) {
- final View viewPos = mLayout.findViewById(R.id.coordinatorLayout);
- Toast.makeText(viewPos.getContext(), text, Toast.LENGTH_SHORT).show();
+ /** Shows hint text on a minimal Snackbar-like text view. */
+ public void showSnackbar(String text) {
+ final int animationDurationMs = 300;
+ final int timeoutDurationMs = mAccessibilityManager.getRecommendedTimeoutMillis(2000,
+ AccessibilityManager.FLAG_CONTENT_TEXT);
+
+ final TextView snackbar = mLayout.findViewById(R.id.snackbar);
+ snackbar.setText(text);
+
+ // Remove any existing fade-out animation before starting any new animations.
+ mHandler.removeCallbacksAndMessages(null);
+
+ if (snackbar.getVisibility() != View.VISIBLE) {
+ snackbar.setAlpha(0f);
+ snackbar.setVisibility(View.VISIBLE);
+ snackbar.animate().alpha(1f).setDuration(animationDurationMs).setListener(null);
+ }
+ mHandler.postDelayed(() -> snackbar.animate().alpha(0f).setDuration(
+ animationDurationMs).setListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(@NonNull Animator animation) {
+ snackbar.setVisibility(View.GONE);
+ }
+ }), timeoutDurationMs);
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardQuickAffordancePreviewConstants.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardQuickAffordancePreviewConstants.kt
index 18e8a96..bf922bc 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardQuickAffordancePreviewConstants.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardQuickAffordancePreviewConstants.kt
@@ -21,4 +21,5 @@
const val MESSAGE_ID_SLOT_SELECTED = 1337
const val KEY_SLOT_ID = "slot_id"
const val KEY_INITIALLY_SELECTED_SLOT_ID = "initially_selected_slot_id"
+ const val KEY_HIGHLIGHT_QUICK_AFFORDANCES = "highlight_quick_affordances"
}
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 3d3ccf4..a38afdf 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -197,13 +197,10 @@
-packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesManager.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLogger.kt
-packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
-packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -611,7 +608,6 @@
-packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesManagerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
-packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLoggerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
index 314c736..db88b59 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
@@ -171,7 +171,7 @@
a1 = TonalSpec(HueSource(), ChromaConstant(36.0)),
a2 = TonalSpec(HueSource(), ChromaConstant(16.0)),
a3 = TonalSpec(HueAdd(60.0), ChromaConstant(24.0)),
- n1 = TonalSpec(HueSource(), ChromaConstant(4.0)),
+ n1 = TonalSpec(HueSource(), ChromaConstant(6.0)),
n2 = TonalSpec(HueSource(), ChromaConstant(8.0))
)),
VIBRANT(CoreSpec(
diff --git a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml
index acd2462..7d03b0d 100644
--- a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml
+++ b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml
@@ -21,7 +21,7 @@
<item android:state_selected="true">
<shape android:shape="oval">
<stroke
- android:color="@color/control_primary_text"
+ android:color="?android:attr/textColorPrimary"
android:width="2dp"/>
<size
android:width="@dimen/keyguard_affordance_fixed_width"
diff --git a/packages/SystemUI/res/xml/qs_header.xml b/packages/SystemUI/res/xml/qs_header.xml
index d97031f..52a98984 100644
--- a/packages/SystemUI/res/xml/qs_header.xml
+++ b/packages/SystemUI/res/xml/qs_header.xml
@@ -41,9 +41,6 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/privacy_container"
app:layout_constraintBottom_toBottomOf="@id/carrier_group"
- app:layout_constraintEnd_toStartOf="@id/carrier_group"
- app:layout_constraintHorizontal_bias="0"
- app:layout_constraintHorizontal_chainStyle="spread_inside"
/>
<Transform
android:scaleX="2.57"
@@ -62,18 +59,18 @@
/>
</Constraint>
+ <!-- LargeScreenShadeHeaderController helps with managing clock width to layout this view -->
<Constraint
android:id="@+id/carrier_group">
<Layout
- app:layout_constraintWidth_min="48dp"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="@dimen/large_screen_shade_header_min_height"
- app:layout_constraintStart_toEndOf="@id/clock"
+ app:layout_constraintWidth_min="48dp"
+ app:layout_constraintWidth_default="wrap"
+ app:layout_constraintStart_toStartOf="@id/clock"
app:layout_constraintTop_toBottomOf="@id/privacy_container"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHorizontal_bias="1"
app:layout_constraintBottom_toTopOf="@id/batteryRemainingIcon"
- app:layout_constraintHorizontal_chainStyle="spread_inside"
/>
<PropertySet
android:alpha="1"
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index e4f85db..5fd2fab 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -235,14 +235,6 @@
}
updateChildren(0 /* translationY */, 1f /* alpha */);
}
-
- private void updateChildren(int translationY, float alpha) {
- for (int i = 0; i < KeyguardSecurityContainer.this.getChildCount(); ++i) {
- View child = KeyguardSecurityContainer.this.getChildAt(i);
- child.setTranslationY(translationY);
- child.setAlpha(alpha);
- }
- }
};
private final OnBackAnimationCallback mBackCallback = new OnBackAnimationCallback() {
@@ -594,6 +586,7 @@
* This will run when the bouncer shows in all cases except when the user drags the bouncer up.
*/
public void startAppearAnimation(SecurityMode securityMode) {
+ updateChildren(0 /* translationY */, 1f /* alpha */);
mViewMode.startAppearAnimation(securityMode);
}
@@ -777,6 +770,14 @@
setScaleY(scale);
}
+ private void updateChildren(int translationY, float alpha) {
+ for (int i = 0; i < getChildCount(); ++i) {
+ View child = getChildAt(i);
+ child.setTranslationY(translationY);
+ child.setAlpha(alpha);
+ }
+ }
+
/**
* Enscapsulates the differences between bouncer modes for the container.
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 9d6bb08..9eb7e2cd 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -26,7 +26,6 @@
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_PERMANENT;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_TIMED;
import static android.hardware.biometrics.BiometricConstants.LockoutMode;
-import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START;
import static android.hardware.biometrics.BiometricSourceType.FACE;
import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
@@ -353,7 +352,6 @@
private final Executor mBackgroundExecutor;
private final SensorPrivacyManager mSensorPrivacyManager;
private final ActiveUnlockConfig mActiveUnlockConfig;
- private final PowerManager mPowerManager;
private final IDreamManager mDreamManager;
private final TelephonyManager mTelephonyManager;
@Nullable
@@ -361,7 +359,6 @@
@Nullable
private final FaceManager mFaceManager;
private final LockPatternUtils mLockPatternUtils;
- private final boolean mWakeOnFingerprintAcquiredStart;
@VisibleForTesting
@DevicePostureController.DevicePostureInt
protected int mConfigFaceAuthSupportedPosture;
@@ -885,11 +882,6 @@
private void handleFingerprintAcquired(
@BiometricFingerprintConstants.FingerprintAcquired int acquireInfo) {
Assert.isMainThread();
- if (mWakeOnFingerprintAcquiredStart && acquireInfo == FINGERPRINT_ACQUIRED_START) {
- mPowerManager.wakeUp(
- SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_BIOMETRIC,
- "com.android.systemui.keyguard:FINGERPRINT_ACQUIRED_START");
- }
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -1538,6 +1530,7 @@
@VisibleForTesting
void setAssistantVisible(boolean assistantVisible) {
mAssistantVisible = assistantVisible;
+ mLogger.logAssistantVisible(mAssistantVisible);
updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
FACE_AUTH_UPDATED_ASSISTANT_VISIBILITY_CHANGED);
if (mAssistantVisible) {
@@ -2050,7 +2043,6 @@
UiEventLogger uiEventLogger,
// This has to be a provider because SessionTracker depends on KeyguardUpdateMonitor :(
Provider<SessionTracker> sessionTrackerProvider,
- PowerManager powerManager,
TrustManager trustManager,
SubscriptionManager subscriptionManager,
UserManager userManager,
@@ -2087,7 +2079,6 @@
mLogger = logger;
mUiEventLogger = uiEventLogger;
mSessionTrackerProvider = sessionTrackerProvider;
- mPowerManager = powerManager;
mTrustManager = trustManager;
mUserManager = userManager;
mDreamManager = dreamManager;
@@ -2098,8 +2089,6 @@
mFpm = fingerprintManager;
mFaceManager = faceManager;
mActiveUnlockConfig.setKeyguardUpdateMonitor(this);
- mWakeOnFingerprintAcquiredStart = context.getResources()
- .getBoolean(com.android.internal.R.bool.kg_wake_on_acquire_start);
mFaceAcquiredInfoIgnoreList = Arrays.stream(
mContext.getResources().getIntArray(
R.array.config_face_acquire_device_entry_ignorelist))
@@ -2680,7 +2669,10 @@
private boolean shouldListenForFaceAssistant() {
BiometricAuthenticated face = mUserFaceAuthenticated.get(getCurrentUser());
- return mAssistantVisible && mKeyguardOccluded
+ return mAssistantVisible
+ // There can be intermediate states where mKeyguardShowing is false but
+ // mKeyguardOccluded is true, we don't want to run face auth in such a scenario.
+ && (mKeyguardShowing && mKeyguardOccluded)
&& !(face != null && face.mAuthenticated)
&& !mUserHasTrust.get(getCurrentUser(), false);
}
@@ -3681,6 +3673,7 @@
if (info == null) {
return;
}
+ mLogger.logTaskStackChangedForAssistant(info.visible);
mHandler.sendMessage(mHandler.obtainMessage(MSG_ASSISTANT_STACK_CHANGED,
info.visible));
} catch (RemoteException e) {
@@ -3879,7 +3872,6 @@
pw.println(" getUserHasTrust()=" + getUserHasTrust(getCurrentUser()));
pw.println(" getUserUnlockedWithBiometric()="
+ getUserUnlockedWithBiometric(getCurrentUser()));
- pw.println(" mWakeOnFingerprintAcquiredStart=" + mWakeOnFingerprintAcquiredStart);
pw.println(" SIM States:");
for (SimData data : mSimDatas.values()) {
pw.println(" " + data.toString());
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 5b42455..201a1d9 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -431,4 +431,20 @@
str1 = PowerManager.wakeReasonToString(pmWakeReason)
}, { "Skip updating face listening state on wakeup from $str1"})
}
+
+ fun logTaskStackChangedForAssistant(assistantVisible: Boolean) {
+ logBuffer.log(TAG, VERBOSE, {
+ bool1 = assistantVisible
+ }, {
+ "TaskStackChanged for ACTIVITY_TYPE_ASSISTANT, assistant visible: $bool1"
+ })
+ }
+
+ fun logAssistantVisible(assistantVisible: Boolean) {
+ logBuffer.log(TAG, VERBOSE, {
+ bool1 = assistantVisible
+ }, {
+ "Updating mAssistantVisible to new value: $bool1"
+ })
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 108a0e8..6012e3e 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -265,10 +265,11 @@
// 600- status bar
// TODO(b/256614753): Tracking Bug
- val NEW_STATUS_BAR_MOBILE_ICONS = unreleasedFlag(606, "new_status_bar_mobile_icons")
+ val NEW_STATUS_BAR_MOBILE_ICONS =
+ unreleasedFlag(606, "new_status_bar_mobile_icons", teamfood = true)
// TODO(b/256614210): Tracking Bug
- val NEW_STATUS_BAR_WIFI_ICON = unreleasedFlag(607, "new_status_bar_wifi_icon")
+ val NEW_STATUS_BAR_WIFI_ICON = unreleasedFlag(607, "new_status_bar_wifi_icon", teamfood = true)
// TODO(b/256614751): Tracking Bug
val NEW_STATUS_BAR_MOBILE_ICONS_BACKEND =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index d020529..e9d7a5b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -66,6 +66,8 @@
object KeyguardBottomAreaViewBinder {
private const val EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS = 250L
+ private const val SCALE_SELECTED_BUTTON = 1.23f
+ private const val DIM_ALPHA = 0.3f
/**
* Defines interface for an object that acts as the binding between the view and its view-model.
@@ -315,6 +317,12 @@
} else {
null
}
+ view
+ .animate()
+ .scaleX(if (viewModel.isSelected) SCALE_SELECTED_BUTTON else 1f)
+ .scaleY(if (viewModel.isSelected) SCALE_SELECTED_BUTTON else 1f)
+ .alpha(if (viewModel.isDimmed) DIM_ALPHA else 1f)
+ .start()
view.isClickable = viewModel.isClickable
if (viewModel.isClickable) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index a5ae8ba5..8808574 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -24,7 +24,6 @@
import android.hardware.display.DisplayManager
import android.os.Bundle
import android.os.IBinder
-import android.view.Gravity
import android.view.LayoutInflater
import android.view.SurfaceControlViewHost
import android.view.View
@@ -65,6 +64,11 @@
val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN)
private val width: Int = bundle.getInt(KEY_VIEW_WIDTH)
private val height: Int = bundle.getInt(KEY_VIEW_HEIGHT)
+ private val shouldHighlightSelectedAffordance: Boolean =
+ bundle.getBoolean(
+ KeyguardQuickAffordancePreviewConstants.KEY_HIGHLIGHT_QUICK_AFFORDANCES,
+ false,
+ )
private var host: SurfaceControlViewHost
@@ -82,6 +86,7 @@
bundle.getString(
KeyguardQuickAffordancePreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID,
),
+ shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
)
runBlocking(mainDispatcher) {
host =
@@ -154,8 +159,7 @@
bottomAreaView,
FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
- FrameLayout.LayoutParams.WRAP_CONTENT,
- Gravity.BOTTOM,
+ FrameLayout.LayoutParams.MATCH_PARENT,
),
)
}
@@ -195,7 +199,13 @@
?.events
?.onTargetRegionChanged(KeyguardClockSwitch.getLargeClockRegion(parentView))
clockView?.let { parentView.removeView(it) }
- clockView = clockController.clock?.largeClock?.view?.apply { parentView.addView(this) }
+ clockView =
+ clockController.clock?.largeClock?.view?.apply {
+ if (shouldHighlightSelectedAffordance) {
+ alpha = DIM_ALPHA
+ }
+ parentView.addView(this)
+ }
}
companion object {
@@ -203,5 +213,7 @@
private const val KEY_VIEW_WIDTH = "width"
private const val KEY_VIEW_HEIGHT = "height"
private const val KEY_DISPLAY_ID = "display_id"
+
+ private const val DIM_ALPHA = 0.3f
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
index 5d85680..1e3b60c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
@@ -45,12 +45,17 @@
private val bottomAreaInteractor: KeyguardBottomAreaInteractor,
private val burnInHelperWrapper: BurnInHelperWrapper,
) {
+ data class PreviewMode(
+ val isInPreviewMode: Boolean = false,
+ val shouldHighlightSelectedAffordance: Boolean = false,
+ )
+
/**
* Whether this view-model instance is powering the preview experience that renders exclusively
* in the wallpaper picker application. This should _always_ be `false` for the real lock screen
* experience.
*/
- private val isInPreviewMode = MutableStateFlow(false)
+ private val previewMode = MutableStateFlow(PreviewMode())
/**
* ID of the slot that's currently selected in the preview that renders exclusively in the
@@ -87,8 +92,8 @@
keyguardInteractor.isDozing.map { !it }.distinctUntilChanged()
/** An observable for the alpha level for the entire bottom area. */
val alpha: Flow<Float> =
- isInPreviewMode.flatMapLatest { isInPreviewMode ->
- if (isInPreviewMode) {
+ previewMode.flatMapLatest {
+ if (it.isInPreviewMode) {
flowOf(1f)
} else {
bottomAreaInteractor.alpha.distinctUntilChanged()
@@ -129,9 +134,18 @@
* lock screen.
*
* @param initiallySelectedSlotId The ID of the initial slot to render as the selected one.
+ * @param shouldHighlightSelectedAffordance Whether the selected quick affordance should be
+ * highlighted (while all others are dimmed to make the selected one stand out).
*/
- fun enablePreviewMode(initiallySelectedSlotId: String?) {
- isInPreviewMode.value = true
+ fun enablePreviewMode(
+ initiallySelectedSlotId: String?,
+ shouldHighlightSelectedAffordance: Boolean,
+ ) {
+ previewMode.value =
+ PreviewMode(
+ isInPreviewMode = true,
+ shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
+ )
onPreviewSlotSelected(
initiallySelectedSlotId ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
)
@@ -150,9 +164,9 @@
private fun button(
position: KeyguardQuickAffordancePosition
): Flow<KeyguardQuickAffordanceViewModel> {
- return isInPreviewMode.flatMapLatest { isInPreviewMode ->
+ return previewMode.flatMapLatest { previewMode ->
combine(
- if (isInPreviewMode) {
+ if (previewMode.isInPreviewMode) {
quickAffordanceInteractor.quickAffordanceAlwaysVisible(position = position)
} else {
quickAffordanceInteractor.quickAffordance(position = position)
@@ -161,11 +175,18 @@
areQuickAffordancesFullyOpaque,
selectedPreviewSlotId,
) { model, animateReveal, isFullyOpaque, selectedPreviewSlotId ->
+ val isSelected = selectedPreviewSlotId == position.toSlotId()
model.toViewModel(
- animateReveal = !isInPreviewMode && animateReveal,
- isClickable = isFullyOpaque && !isInPreviewMode,
+ animateReveal = !previewMode.isInPreviewMode && animateReveal,
+ isClickable = isFullyOpaque && !previewMode.isInPreviewMode,
isSelected =
- (isInPreviewMode && selectedPreviewSlotId == position.toSlotId()),
+ previewMode.isInPreviewMode &&
+ previewMode.shouldHighlightSelectedAffordance &&
+ isSelected,
+ isDimmed =
+ previewMode.isInPreviewMode &&
+ previewMode.shouldHighlightSelectedAffordance &&
+ !isSelected,
)
}
.distinctUntilChanged()
@@ -176,6 +197,7 @@
animateReveal: Boolean,
isClickable: Boolean,
isSelected: Boolean,
+ isDimmed: Boolean,
): KeyguardQuickAffordanceViewModel {
return when (this) {
is KeyguardQuickAffordanceModel.Visible ->
@@ -194,6 +216,7 @@
isActivated = activationState is ActivationState.Active,
isSelected = isSelected,
useLongPress = quickAffordanceInteractor.useLongPress,
+ isDimmed = isDimmed,
)
is KeyguardQuickAffordanceModel.Hidden -> KeyguardQuickAffordanceViewModel()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
index cf3a6da..cb68a82 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
@@ -31,6 +31,7 @@
val isActivated: Boolean = false,
val isSelected: Boolean = false,
val useLongPress: Boolean = false,
+ val isDimmed: Boolean = false,
) {
data class OnClickedParameters(
val configKey: String,
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index e3c44d1..f61d135 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -200,28 +200,6 @@
}
/**
- * Provides a logging buffer for logs related to the media tap-to-transfer chip on the sender
- * device. See {@link com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogger}.
- */
- @Provides
- @SysUISingleton
- @MediaTttSenderLogBuffer
- public static LogBuffer provideMediaTttSenderLogBuffer(LogBufferFactory factory) {
- return factory.create("MediaTttSender", 20);
- }
-
- /**
- * Provides a logging buffer for logs related to the media tap-to-transfer chip on the receiver
- * device. See {@link com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogger}.
- */
- @Provides
- @SysUISingleton
- @MediaTttReceiverLogBuffer
- public static LogBuffer provideMediaTttReceiverLogBuffer(LogBufferFactory factory) {
- return factory.create("MediaTttReceiver", 20);
- }
-
- /**
* Provides a logging buffer for logs related to the media mute-await connections. See
* {@link com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManager}.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index bb833df..9ae4577 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -17,8 +17,7 @@
package com.android.systemui.media.dagger;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.log.dagger.MediaTttReceiverLogBuffer;
-import com.android.systemui.log.dagger.MediaTttSenderLogBuffer;
+import com.android.systemui.log.LogBufferFactory;
import com.android.systemui.media.controls.pipeline.MediaDataManager;
import com.android.systemui.media.controls.ui.MediaHierarchyManager;
import com.android.systemui.media.controls.ui.MediaHost;
@@ -29,12 +28,9 @@
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
import com.android.systemui.media.taptotransfer.MediaTttFlags;
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger;
-import com.android.systemui.media.taptotransfer.receiver.ChipReceiverInfo;
-import com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogger;
-import com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogger;
+import com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogBuffer;
+import com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogBuffer;
import com.android.systemui.plugins.log.LogBuffer;
-import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo;
import java.util.Optional;
@@ -94,22 +90,22 @@
return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager);
}
+ /** Provides a logging buffer related to the media tap-to-transfer chip on the sender device. */
@Provides
@SysUISingleton
- @MediaTttSenderLogger
- static MediaTttLogger<ChipbarInfo> providesMediaTttSenderLogger(
- @MediaTttSenderLogBuffer LogBuffer buffer
- ) {
- return new MediaTttLogger<>("Sender", buffer);
+ @MediaTttSenderLogBuffer
+ static LogBuffer provideMediaTttSenderLogBuffer(LogBufferFactory factory) {
+ return factory.create("MediaTttSender", 30);
}
+ /**
+ * Provides a logging buffer related to the media tap-to-transfer chip on the receiver device.
+ */
@Provides
@SysUISingleton
- @MediaTttReceiverLogger
- static MediaTttLogger<ChipReceiverInfo> providesMediaTttReceiverLogger(
- @MediaTttReceiverLogBuffer LogBuffer buffer
- ) {
- return new MediaTttLogger<>("Receiver", buffer);
+ @MediaTttReceiverLogBuffer
+ static LogBuffer provideMediaTttReceiverLogBuffer(LogBufferFactory factory) {
+ return factory.create("MediaTttReceiver", 20);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt
deleted file mode 100644
index 8aef938..0000000
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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.media.taptotransfer.common
-
-import com.android.systemui.plugins.log.LogBuffer
-import com.android.systemui.plugins.log.LogLevel
-import com.android.systemui.temporarydisplay.TemporaryViewInfo
-import com.android.systemui.temporarydisplay.TemporaryViewLogger
-
-/**
- * A logger for media tap-to-transfer events.
- *
- * @param deviceTypeTag the type of device triggering the logs -- "Sender" or "Receiver".
- *
- * TODO(b/245610654): We should de-couple the sender and receiver loggers, since they're vastly
- * different experiences.
- */
-class MediaTttLogger<T : TemporaryViewInfo>(
- deviceTypeTag: String,
- buffer: LogBuffer
-) : TemporaryViewLogger<T>(buffer, BASE_TAG + deviceTypeTag) {
- /** Logs a change in the chip state for the given [mediaRouteId]. */
- fun logStateChange(stateName: String, mediaRouteId: String, packageName: String?) {
- buffer.log(
- tag,
- LogLevel.DEBUG,
- {
- str1 = stateName
- str2 = mediaRouteId
- str3 = packageName
- },
- { "State changed to $str1 for ID=$str2 package=$str3" }
- )
- }
-
- /**
- * Logs an error in trying to update to [displayState].
- *
- * [displayState] is either a [android.app.StatusBarManager.MediaTransferSenderState] or
- * a [android.app.StatusBarManager.MediaTransferReceiverState].
- */
- fun logStateChangeError(displayState: Int) {
- buffer.log(
- tag,
- LogLevel.ERROR,
- { int1 = displayState },
- { "Cannot display state=$int1; aborting" }
- )
- }
-
- /**
- * Logs an invalid sender state transition error in trying to update to [desiredState].
- *
- * @param currentState the previous state of the chip.
- * @param desiredState the new state of the chip.
- */
- fun logInvalidStateTransitionError(
- currentState: String,
- desiredState: String
- ) {
- buffer.log(
- tag,
- LogLevel.ERROR,
- {
- str1 = currentState
- str2 = desiredState
- },
- { "Cannot display state=$str2 after state=$str1; invalid transition" }
- )
- }
-
- /** Logs that we couldn't find information for [packageName]. */
- fun logPackageNotFound(packageName: String) {
- buffer.log(
- tag,
- LogLevel.DEBUG,
- { str1 = packageName },
- { "Package $str1 could not be found" }
- )
- }
-
- /**
- * Logs that a removal request has been bypassed (ignored).
- *
- * @param removalReason the reason that the chip removal was requested.
- * @param bypassReason the reason that the request was bypassed.
- */
- fun logRemovalBypass(removalReason: String, bypassReason: String) {
- buffer.log(
- tag,
- LogLevel.DEBUG,
- {
- str1 = removalReason
- str2 = bypassReason
- },
- { "Chip removal requested due to $str1; however, removal was ignored because $str2" })
- }
-}
-
-private const val BASE_TAG = "MediaTtt"
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt
new file mode 100644
index 0000000..0e839c6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.media.taptotransfer.common
+
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+
+/** A helper for logging media tap-to-transfer events. */
+object MediaTttLoggerUtils {
+ fun logStateChange(
+ buffer: LogBuffer,
+ tag: String,
+ stateName: String,
+ mediaRouteId: String,
+ packageName: String?,
+ ) {
+ buffer.log(
+ tag,
+ LogLevel.DEBUG,
+ {
+ str1 = stateName
+ str2 = mediaRouteId
+ str3 = packageName
+ },
+ { "State changed to $str1 for ID=$str2 package=$str3" }
+ )
+ }
+
+ fun logStateChangeError(buffer: LogBuffer, tag: String, displayState: Int) {
+ buffer.log(
+ tag,
+ LogLevel.ERROR,
+ { int1 = displayState },
+ { "Cannot display state=$int1; aborting" }
+ )
+ }
+
+ fun logPackageNotFound(buffer: LogBuffer, tag: String, packageName: String) {
+ buffer.log(
+ tag,
+ LogLevel.DEBUG,
+ { str1 = packageName },
+ { "Package $str1 could not be found" }
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
index 066c185..a3ae943 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
@@ -25,7 +25,6 @@
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.TintedIcon
-import com.android.systemui.temporarydisplay.TemporaryViewInfo
/** Utility methods for media tap-to-transfer. */
class MediaTttUtils {
@@ -43,12 +42,13 @@
* default name and icon if we can't find the app name/icon.
*
* @param appPackageName the package name of the app playing the media.
- * @param logger the logger to use for any errors.
+ * @param onPackageNotFoundException a function run if a
+ * [PackageManager.NameNotFoundException] occurs.
*/
fun getIconInfoFromPackageName(
context: Context,
appPackageName: String?,
- logger: MediaTttLogger<out TemporaryViewInfo>
+ onPackageNotFoundException: () -> Unit,
): IconInfo {
if (appPackageName != null) {
val packageManager = context.packageManager
@@ -70,7 +70,7 @@
isAppIcon = true
)
} catch (e: PackageManager.NameNotFoundException) {
- logger.logPackageNotFound(appPackageName)
+ onPackageNotFoundException.invoke()
}
}
return IconInfo(
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 60dd5da..34bf74fa 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -40,7 +40,6 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.taptotransfer.MediaTttFlags
import com.android.systemui.media.taptotransfer.common.MediaTttIcon
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.media.taptotransfer.common.MediaTttUtils
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -65,7 +64,7 @@
open class MediaTttChipControllerReceiver @Inject constructor(
private val commandQueue: CommandQueue,
context: Context,
- @MediaTttReceiverLogger logger: MediaTttLogger<ChipReceiverInfo>,
+ logger: MediaTttReceiverLogger,
windowManager: WindowManager,
mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
@@ -79,7 +78,7 @@
wakeLockBuilder: WakeLock.Builder,
systemClock: SystemClock,
private val rippleController: MediaTttReceiverRippleController,
-) : TemporaryViewDisplayController<ChipReceiverInfo, MediaTttLogger<ChipReceiverInfo>>(
+) : TemporaryViewDisplayController<ChipReceiverInfo, MediaTttReceiverLogger>(
context,
logger,
windowManager,
@@ -173,9 +172,10 @@
}
override fun updateView(newInfo: ChipReceiverInfo, currentView: ViewGroup) {
- var iconInfo = MediaTttUtils.getIconInfoFromPackageName(
- context, newInfo.routeInfo.clientPackageName, logger
- )
+ val packageName = newInfo.routeInfo.clientPackageName
+ var iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, packageName) {
+ logger.logPackageNotFound(packageName)
+ }
if (newInfo.appNameOverride != null) {
iconInfo = iconInfo.copy(
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttReceiverLogBuffer.java b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogBuffer.java
similarity index 85%
rename from packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttReceiverLogBuffer.java
rename to packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogBuffer.java
index 1570d43..67e464c 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttReceiverLogBuffer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogBuffer.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.log.dagger;
+package com.android.systemui.media.taptotransfer.receiver;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@@ -26,8 +26,7 @@
import javax.inject.Qualifier;
/**
- * A {@link LogBuffer} for
- * {@link com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogger}.
+ * A {@link LogBuffer} for receiver logs.
*/
@Qualifier
@Documented
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt
index 54fc48d..b0c6257 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLogger.kt
@@ -13,14 +13,44 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package com.android.systemui.media.taptotransfer.receiver
-import java.lang.annotation.Documented
-import java.lang.annotation.Retention
-import java.lang.annotation.RetentionPolicy
-import javax.inject.Qualifier
+import android.app.StatusBarManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.taptotransfer.common.MediaTttLoggerUtils
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.temporarydisplay.TemporaryViewLogger
+import javax.inject.Inject
-@Qualifier
-@Documented
-@Retention(RetentionPolicy.RUNTIME)
-annotation class MediaTttReceiverLogger
+/** A logger for all events related to the media tap-to-transfer receiver experience. */
+@SysUISingleton
+class MediaTttReceiverLogger
+@Inject
+constructor(
+ @MediaTttReceiverLogBuffer buffer: LogBuffer,
+) : TemporaryViewLogger<ChipReceiverInfo>(buffer, TAG) {
+
+ /** Logs a change in the chip state for the given [mediaRouteId]. */
+ fun logStateChange(
+ stateName: String,
+ mediaRouteId: String,
+ packageName: String?,
+ ) {
+ MediaTttLoggerUtils.logStateChange(buffer, TAG, stateName, mediaRouteId, packageName)
+ }
+
+ /** Logs an error in trying to update to [displayState]. */
+ fun logStateChangeError(@StatusBarManager.MediaTransferReceiverState displayState: Int) {
+ MediaTttLoggerUtils.logStateChangeError(buffer, TAG, displayState)
+ }
+
+ /** Logs that we couldn't find information for [packageName]. */
+ fun logPackageNotFound(packageName: String) {
+ MediaTttLoggerUtils.logPackageNotFound(buffer, TAG, packageName)
+ }
+
+ companion object {
+ private const val TAG = "MediaTttReceiver"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
index 902a10a0..89ca5d3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
@@ -23,11 +23,12 @@
import com.android.internal.logging.UiEventLogger
import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.CoreStartable
+import com.android.systemui.Dumpable
import com.android.systemui.R
import com.android.systemui.common.shared.model.Text
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
import com.android.systemui.media.taptotransfer.MediaTttFlags
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.media.taptotransfer.common.MediaTttUtils
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
@@ -35,6 +36,7 @@
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import com.android.systemui.temporarydisplay.chipbar.ChipbarEndItem
import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo
+import java.io.PrintWriter
import javax.inject.Inject
/**
@@ -48,14 +50,13 @@
private val chipbarCoordinator: ChipbarCoordinator,
private val commandQueue: CommandQueue,
private val context: Context,
- @MediaTttSenderLogger private val logger: MediaTttLogger<ChipbarInfo>,
+ private val dumpManager: DumpManager,
+ private val logger: MediaTttSenderLogger,
private val mediaTttFlags: MediaTttFlags,
private val uiEventLogger: MediaTttSenderUiEventLogger,
-) : CoreStartable {
+) : CoreStartable, Dumpable {
- private var displayedState: ChipStateSender? = null
// A map to store current chip state per id.
- // TODO(b/265455911): Log whenever we add or remove from the store.
private var stateMap: MutableMap<String, ChipStateSender> = mutableMapOf()
private val commandQueueCallbacks =
@@ -76,6 +77,7 @@
override fun start() {
if (mediaTttFlags.isMediaTttEnabled()) {
commandQueue.addCallback(commandQueueCallbacks)
+ dumpManager.registerNormalDumpable(this)
}
}
@@ -93,11 +95,11 @@
return
}
- val currentState = stateMap[routeInfo.id]
- if (!ChipStateSender.isValidStateTransition(currentState, chipState)) {
+ val currentStateForId: ChipStateSender? = stateMap[routeInfo.id]
+ if (!ChipStateSender.isValidStateTransition(currentStateForId, chipState)) {
// ChipStateSender.FAR_FROM_RECEIVER is the default state when there is no state.
logger.logInvalidStateTransitionError(
- currentState = currentState?.name ?: ChipStateSender.FAR_FROM_RECEIVER.name,
+ currentState = currentStateForId?.name ?: ChipStateSender.FAR_FROM_RECEIVER.name,
chipState.name
)
return
@@ -105,30 +107,29 @@
uiEventLogger.logSenderStateChange(chipState)
if (chipState == ChipStateSender.FAR_FROM_RECEIVER) {
- // No need to store the state since it is the default state
- removeIdFromStore(routeInfo.id)
- // Return early if we're not displaying a chip anyway
- val currentDisplayedState = displayedState ?: return
+ // Return early if we're not displaying a chip for this ID anyway
+ if (currentStateForId == null) return
val removalReason = ChipStateSender.FAR_FROM_RECEIVER.name
if (
- currentDisplayedState.transferStatus == TransferStatus.IN_PROGRESS ||
- currentDisplayedState.transferStatus == TransferStatus.SUCCEEDED
+ currentStateForId.transferStatus == TransferStatus.IN_PROGRESS ||
+ currentStateForId.transferStatus == TransferStatus.SUCCEEDED
) {
// Don't remove the chip if we're in progress or succeeded, since the user should
// still be able to see the status of the transfer.
logger.logRemovalBypass(
removalReason,
- bypassReason = "transferStatus=${currentDisplayedState.transferStatus.name}"
+ bypassReason = "transferStatus=${currentStateForId.transferStatus.name}"
)
return
}
- displayedState = null
+ // No need to store the state since it is the default state
+ removeIdFromStore(routeInfo.id, reason = removalReason)
chipbarCoordinator.removeView(routeInfo.id, removalReason)
} else {
stateMap[routeInfo.id] = chipState
- displayedState = chipState
+ logger.logStateMap(stateMap)
chipbarCoordinator.registerListener(displayListener)
chipbarCoordinator.displayView(
createChipbarInfo(
@@ -150,7 +151,7 @@
routeInfo: MediaRoute2Info,
undoCallback: IUndoMediaTransferCallback?,
context: Context,
- logger: MediaTttLogger<ChipbarInfo>,
+ logger: MediaTttSenderLogger,
): ChipbarInfo {
val packageName = routeInfo.clientPackageName
val otherDeviceName =
@@ -159,12 +160,14 @@
} else {
routeInfo.name.toString()
}
+ val icon =
+ MediaTttUtils.getIconInfoFromPackageName(context, packageName) {
+ logger.logPackageNotFound(packageName)
+ }
return ChipbarInfo(
// Display the app's icon as the start icon
- startIcon =
- MediaTttUtils.getIconInfoFromPackageName(context, packageName, logger)
- .toTintedIcon(),
+ startIcon = icon.toTintedIcon(),
text = chipStateSender.getChipTextString(context, otherDeviceName),
endItem =
when (chipStateSender.endItem) {
@@ -231,12 +234,19 @@
}
private val displayListener =
- TemporaryViewDisplayController.Listener { id -> removeIdFromStore(id) }
+ TemporaryViewDisplayController.Listener { id, reason -> removeIdFromStore(id, reason) }
- private fun removeIdFromStore(id: String) {
+ private fun removeIdFromStore(id: String, reason: String) {
+ logger.logStateMapRemoval(id, reason)
stateMap.remove(id)
+ logger.logStateMap(stateMap)
if (stateMap.isEmpty()) {
chipbarCoordinator.unregisterListener(displayListener)
}
}
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println("Current sender states:")
+ pw.println(stateMap.toString())
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttSenderLogBuffer.java b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogBuffer.java
similarity index 86%
rename from packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttSenderLogBuffer.java
rename to packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogBuffer.java
index bf216c6..a262e97 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTttSenderLogBuffer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogBuffer.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.log.dagger;
+package com.android.systemui.media.taptotransfer.sender;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@@ -26,8 +26,7 @@
import javax.inject.Qualifier;
/**
- * A {@link LogBuffer} for
- * {@link com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogger}.
+ * A {@link LogBuffer} for sender logs.
*/
@Qualifier
@Documented
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
index 4393af9..964a95b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
@@ -13,14 +13,102 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package com.android.systemui.media.taptotransfer.sender
-import java.lang.annotation.Documented
-import java.lang.annotation.Retention
-import java.lang.annotation.RetentionPolicy
-import javax.inject.Qualifier
+import android.app.StatusBarManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.taptotransfer.common.MediaTttLoggerUtils
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+import javax.inject.Inject
-@Qualifier
-@Documented
-@Retention(RetentionPolicy.RUNTIME)
-annotation class MediaTttSenderLogger
+/** A logger for all events related to the media tap-to-transfer sender experience. */
+@SysUISingleton
+class MediaTttSenderLogger
+@Inject
+constructor(
+ @MediaTttSenderLogBuffer private val buffer: LogBuffer,
+) {
+ /** Logs a change in the chip state for the given [mediaRouteId]. */
+ fun logStateChange(
+ stateName: String,
+ mediaRouteId: String,
+ packageName: String?,
+ ) {
+ MediaTttLoggerUtils.logStateChange(buffer, TAG, stateName, mediaRouteId, packageName)
+ }
+
+ /** Logs an error in trying to update to [displayState]. */
+ fun logStateChangeError(@StatusBarManager.MediaTransferSenderState displayState: Int) {
+ MediaTttLoggerUtils.logStateChangeError(buffer, TAG, displayState)
+ }
+
+ /** Logs that we couldn't find information for [packageName]. */
+ fun logPackageNotFound(packageName: String) {
+ MediaTttLoggerUtils.logPackageNotFound(buffer, TAG, packageName)
+ }
+
+ /**
+ * Logs an invalid sender state transition error in trying to update to [desiredState].
+ *
+ * @param currentState the previous state of the chip.
+ * @param desiredState the new state of the chip.
+ */
+ fun logInvalidStateTransitionError(currentState: String, desiredState: String) {
+ buffer.log(
+ TAG,
+ LogLevel.ERROR,
+ {
+ str1 = currentState
+ str2 = desiredState
+ },
+ { "Cannot display state=$str2 after state=$str1; invalid transition" }
+ )
+ }
+
+ /**
+ * Logs that a removal request has been bypassed (ignored).
+ *
+ * @param removalReason the reason that the chip removal was requested.
+ * @param bypassReason the reason that the request was bypassed.
+ */
+ fun logRemovalBypass(removalReason: String, bypassReason: String) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = removalReason
+ str2 = bypassReason
+ },
+ { "Chip removal requested due to $str1; however, removal was ignored because $str2" }
+ )
+ }
+
+ /** Logs the current contents of the state map. */
+ fun logStateMap(map: Map<String, ChipStateSender>) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { str1 = map.toString() },
+ { "Current sender states: $str1" }
+ )
+ }
+
+ /** Logs that [id] has been removed from the state map due to [reason]. */
+ fun logStateMapRemoval(id: String, reason: String) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = id
+ str2 = reason
+ },
+ { "State removal: id=$str1 reason=$str2" }
+ )
+ }
+
+ companion object {
+ private const val TAG = "MediaTttSender"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
index b48ea23..be93550 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
@@ -43,6 +43,7 @@
import javax.inject.Inject
private const val TAG = "AutoAddTracker"
+private const val DELIMITER = ","
/**
* Class to track tiles that have been auto-added
@@ -67,7 +68,7 @@
@GuardedBy("autoAdded")
private val autoAdded = ArraySet<String>()
- private var restoredTiles: Set<String>? = null
+ private var restoredTiles: Map<String, AutoTile>? = null
override val currentUserId: Int
get() = userId
@@ -98,25 +99,26 @@
when (intent.getStringExtra(Intent.EXTRA_SETTING_NAME)) {
Settings.Secure.QS_TILES -> {
restoredTiles = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
- ?.split(",")
- ?.toSet()
+ ?.split(DELIMITER)
+ ?.mapIndexed(::AutoTile)
+ ?.associateBy(AutoTile::tileType)
?: run {
Log.w(TAG, "Null restored tiles for user $userId")
- emptySet()
+ emptyMap()
}
}
Settings.Secure.QS_AUTO_ADDED_TILES -> {
- restoredTiles?.let { tiles ->
+ restoredTiles?.let { restoredTiles ->
val restoredAutoAdded = intent
.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
- ?.split(",")
+ ?.split(DELIMITER)
?: emptyList()
val autoAddedBeforeRestore = intent
.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE)
- ?.split(",")
+ ?.split(DELIMITER)
?: emptyList()
- val tilesToRemove = restoredAutoAdded.filter { it !in tiles }
+ val tilesToRemove = restoredAutoAdded.filter { it !in restoredTiles }
if (tilesToRemove.isNotEmpty()) {
qsHost.removeTiles(tilesToRemove)
}
@@ -180,6 +182,9 @@
registerBroadcastReceiver()
}
+ fun getRestoredTilePosition(tile: String): Int =
+ restoredTiles?.get(tile)?.index ?: QSTileHost.POSITION_AT_END
+
/**
* Returns `true` if the tile has been auto-added before
*/
@@ -196,12 +201,12 @@
*/
fun setTileAdded(tile: String) {
val tiles = synchronized(autoAdded) {
- if (autoAdded.add(tile)) {
- getTilesFromListLocked()
- } else {
- null
- }
+ if (autoAdded.add(tile)) {
+ getTilesFromListLocked()
+ } else {
+ null
}
+ }
tiles?.let { saveTiles(it) }
}
@@ -222,7 +227,7 @@
}
private fun getTilesFromListLocked(): String {
- return TextUtils.join(",", autoAdded)
+ return TextUtils.join(DELIMITER, autoAdded)
}
private fun saveTiles(tiles: String) {
@@ -245,7 +250,7 @@
private fun getAdded(): Collection<String> {
val current = secureSettings.getStringForUser(Settings.Secure.QS_AUTO_ADDED_TILES, userId)
- return current?.split(",") ?: emptySet()
+ return current?.split(DELIMITER) ?: emptySet()
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
@@ -281,4 +286,6 @@
)
}
}
+
+ private data class AutoTile(val index: Int, val tileType: String)
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
index 8867637..197232e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
@@ -16,6 +16,7 @@
package com.android.systemui.shade
+import android.animation.Animator
import android.annotation.IdRes
import android.app.StatusBarManager
import android.content.res.Configuration
@@ -45,7 +46,6 @@
import com.android.systemui.qs.carrier.QSCarrierGroup
import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.HEADER_TRANSITION_ID
-import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.LARGE_SCREEN_HEADER_CONSTRAINT
import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QQS_HEADER_CONSTRAINT
import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QS_HEADER_CONSTRAINT
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
@@ -176,9 +176,13 @@
var shadeExpandedFraction = -1f
set(value) {
if (field != value) {
+ val oldAlpha = header.alpha
header.alpha = ShadeInterpolation.getContentAlpha(value)
field = value
- updateVisibility()
+ if ((oldAlpha == 0f && header.alpha > 0f) ||
+ (oldAlpha > 0f && header.alpha == 0f)) {
+ updateVisibility()
+ }
}
}
@@ -305,6 +309,8 @@
val newPivot = if (v.isLayoutRtl) v.width.toFloat() else 0f
v.pivotX = newPivot
v.pivotY = v.height.toFloat() / 2
+
+ qsCarrierGroup.setPaddingRelative((v.width * v.scaleX).toInt(), 0, 0, 0)
}
}
@@ -335,9 +341,28 @@
.setUpdateListener {
updateVisibility()
}
+ .setListener(endAnimationListener)
.start()
}
+ private val endAnimationListener = object : Animator.AnimatorListener {
+ override fun onAnimationCancel(animation: Animator?) {
+ clearListeners()
+ }
+
+ override fun onAnimationEnd(animation: Animator?) {
+ clearListeners()
+ }
+
+ override fun onAnimationRepeat(animation: Animator?) {}
+
+ override fun onAnimationStart(animation: Animator?) {}
+
+ private fun clearListeners() {
+ header.animate().setListener(null).setUpdateListener(null)
+ }
+ }
+
private fun loadConstraints() {
if (header is MotionLayout) {
// Use resources.getXml instead of passing the resource id due to bug b/205018300
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index d11f9da..457d7ba 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -2157,6 +2157,7 @@
}
ValueAnimator animator = createHeightAnimator(target, overshootAmount);
if (expand) {
+ maybeVibrateOnOpening(true /* openingWithTouch */);
if (expandBecauseOfFalsing && vel < 0) {
vel = 0;
}
@@ -2167,6 +2168,7 @@
animator.setDuration(SHADE_OPEN_SPRING_OUT_DURATION);
}
} else {
+ mHasVibratedOnOpen = false;
if (shouldUseDismissingAnimation()) {
if (vel == 0) {
animator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
@@ -6375,7 +6377,7 @@
mShadeLog.logHasVibrated(mHasVibratedOnOpen, mExpandedFraction);
}
addMovement(event);
- if (!isFullyCollapsed()) {
+ if (!isFullyCollapsed() && !isOnKeyguard()) {
maybeVibrateOnOpening(true /* openingWithTouch */);
}
float h = y - mInitialExpandY;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 808638a..8436ff7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -16,27 +16,15 @@
package com.android.systemui.statusbar.notification.dagger;
-import android.app.INotificationManager;
import android.content.Context;
-import android.content.pm.LauncherApps;
-import android.content.pm.ShortcutManager;
-import android.os.Handler;
-import android.view.accessibility.AccessibilityManager;
-import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.settings.UserContextProvider;
-import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeEventsModule;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.NotificationListener;
-import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
@@ -50,7 +38,6 @@
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
-import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.collection.provider.NotificationVisibilityProviderImpl;
import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderModule;
import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
@@ -72,22 +59,17 @@
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl;
-import com.android.systemui.statusbar.notification.row.ChannelEditorDialogController;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.wmshell.BubblesManager;
-import java.util.Optional;
import java.util.concurrent.Executor;
import javax.inject.Provider;
import dagger.Binds;
-import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
@@ -109,48 +91,6 @@
@Binds
StackScrollAlgorithm.BypassController bindBypassController(KeyguardBypassController impl);
- /** Provides an instance of {@link NotificationGutsManager} */
- @SysUISingleton
- @Provides
- static NotificationGutsManager provideNotificationGutsManager(
- Context context,
- Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
- @Main Handler mainHandler,
- @Background Handler bgHandler,
- AccessibilityManager accessibilityManager,
- HighPriorityProvider highPriorityProvider,
- INotificationManager notificationManager,
- PeopleSpaceWidgetManager peopleSpaceWidgetManager,
- LauncherApps launcherApps,
- ShortcutManager shortcutManager,
- ChannelEditorDialogController channelEditorDialogController,
- UserContextProvider contextTracker,
- AssistantFeedbackController assistantFeedbackController,
- Optional<BubblesManager> bubblesManagerOptional,
- UiEventLogger uiEventLogger,
- OnUserInteractionCallback onUserInteractionCallback,
- ShadeController shadeController) {
- return new NotificationGutsManager(
- context,
- centralSurfacesOptionalLazy,
- mainHandler,
- bgHandler,
- accessibilityManager,
- highPriorityProvider,
- notificationManager,
- peopleSpaceWidgetManager,
- launcherApps,
- shortcutManager,
- channelEditorDialogController,
- contextTracker,
- assistantFeedbackController,
- bubblesManagerOptional,
- uiEventLogger,
- onUserInteractionCallback,
- shadeController
- );
- }
-
/** Provides an instance of {@link NotifGutsViewManager} */
@Binds
NotifGutsViewManager bindNotifGutsViewManager(NotificationGutsManager notificationGutsManager);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index ea12b82..37ff11d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -44,12 +44,10 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settingslib.notification.ConversationIconFactory;
-import com.android.systemui.Dependency;
-import com.android.systemui.Dumpable;
import com.android.systemui.R;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dump.DumpManager;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -65,28 +63,29 @@
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener;
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
-import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.wmshell.BubblesManager;
-import java.io.PrintWriter;
import java.util.Optional;
+import javax.inject.Inject;
+
import dagger.Lazy;
/**
* Handles various NotificationGuts related tasks, such as binding guts to a row, opening and
* closing guts, and keeping track of the currently exposed notification guts.
*/
+@SysUISingleton
public class NotificationGutsManager implements NotifGutsViewManager {
private static final String TAG = "NotificationGutsManager";
// Must match constant in Settings. Used to highlight preferences when linking to Settings.
private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
- private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
+ private final MetricsLogger mMetricsLogger;
private final Context mContext;
private final AccessibilityManager mAccessibilityManager;
private final HighPriorityProvider mHighPriorityProvider;
@@ -94,12 +93,9 @@
private final OnUserInteractionCallback mOnUserInteractionCallback;
// Dependencies:
- private final NotificationLockscreenUserManager mLockscreenUserManager =
- Dependency.get(NotificationLockscreenUserManager.class);
- private final StatusBarStateController mStatusBarStateController =
- Dependency.get(StatusBarStateController.class);
- private final DeviceProvisionedController mDeviceProvisionedController =
- Dependency.get(DeviceProvisionedController.class);
+ private final NotificationLockscreenUserManager mLockscreenUserManager;
+ private final StatusBarStateController mStatusBarStateController;
+ private final DeviceProvisionedController mDeviceProvisionedController;
private final AssistantFeedbackController mAssistantFeedbackController;
// which notification is currently being longpress-examined by the user
@@ -124,9 +120,7 @@
private final ShadeController mShadeController;
private NotifGutsViewListener mGutsListener;
- /**
- * Injected constructor. See {@link NotificationsModule}.
- */
+ @Inject
public NotificationGutsManager(Context context,
Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
@Main Handler mainHandler,
@@ -143,7 +137,11 @@
Optional<BubblesManager> bubblesManagerOptional,
UiEventLogger uiEventLogger,
OnUserInteractionCallback onUserInteractionCallback,
- ShadeController shadeController) {
+ ShadeController shadeController,
+ NotificationLockscreenUserManager notificationLockscreenUserManager,
+ StatusBarStateController statusBarStateController,
+ DeviceProvisionedController deviceProvisionedController,
+ MetricsLogger metricsLogger) {
mContext = context;
mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mMainHandler = mainHandler;
@@ -161,6 +159,10 @@
mUiEventLogger = uiEventLogger;
mOnUserInteractionCallback = onUserInteractionCallback;
mShadeController = shadeController;
+ mLockscreenUserManager = notificationLockscreenUserManager;
+ mStatusBarStateController = statusBarStateController;
+ mDeviceProvisionedController = deviceProvisionedController;
+ mMetricsLogger = metricsLogger;
}
public void setUpWithPresenter(NotificationPresenter presenter,
@@ -372,7 +374,8 @@
mDeviceProvisionedController.isDeviceProvisioned(),
row.getIsNonblockable(),
mHighPriorityProvider.isHighPriority(row.getEntry()),
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index ea0060a..8a50f2f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -204,10 +204,11 @@
boolean isDeviceProvisioned,
boolean isNonblockable,
boolean wasShownHighPriority,
- AssistantFeedbackController assistantFeedbackController)
+ AssistantFeedbackController assistantFeedbackController,
+ MetricsLogger metricsLogger)
throws RemoteException {
mINotificationManager = iNotificationManager;
- mMetricsLogger = Dependency.get(MetricsLogger.class);
+ mMetricsLogger = metricsLogger;
mOnUserInteractionCallback = onUserInteractionCallback;
mChannelEditorDialogController = channelEditorDialogController;
mAssistantFeedbackController = assistantFeedbackController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 149ec54..8b1a02b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -279,7 +279,8 @@
public void onManagedProfileChanged() {
if (mManagedProfileController.hasActiveProfile()) {
if (mAutoTracker.isAdded(WORK)) return;
- mHost.addTile(WORK);
+ final int position = mAutoTracker.getRestoredTilePosition(WORK);
+ mHost.addTile(WORK, position);
mAutoTracker.setTileAdded(WORK);
} else {
if (!mAutoTracker.isAdded(WORK)) return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index 510482d..39ad31f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -53,7 +53,7 @@
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
-import com.android.systemui.util.kotlin.pairwiseBy
+import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.settings.GlobalSettings
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -66,10 +66,10 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
@@ -287,22 +287,13 @@
*/
@SuppressLint("MissingPermission")
override val activeSubChangedInGroupEvent =
- flow {
- activeMobileDataSubscriptionId.pairwiseBy { prevVal: Int, newVal: Int ->
- if (!defaultMobileNetworkConnectivity.value.isValidated) {
- return@pairwiseBy
- }
- val prevSub = subscriptionManager.getActiveSubscriptionInfo(prevVal)
- val nextSub = subscriptionManager.getActiveSubscriptionInfo(newVal)
+ activeMobileDataSubscriptionId
+ .pairwise()
+ .mapNotNull { (prevVal: Int, newVal: Int) ->
+ val prevSub = subscriptionManager.getActiveSubscriptionInfo(prevVal)?.groupUuid
+ val nextSub = subscriptionManager.getActiveSubscriptionInfo(newVal)?.groupUuid
- if (prevSub == null || nextSub == null) {
- return@pairwiseBy
- }
-
- if (prevSub.groupUuid != null && prevSub.groupUuid == nextSub.groupUuid) {
- emit(Unit)
- }
- }
+ if (prevSub != null && prevSub == nextSub) Unit else null
}
.flowOn(bgDispatcher)
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
index df8d161..1065d33 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -327,7 +327,7 @@
// appropriately.
activeViews.remove(displayInfo)
listeners.forEach {
- it.onInfoPermanentlyRemoved(id)
+ it.onInfoPermanentlyRemoved(id, removalReason)
}
// No need to time the view out since it's already gone
@@ -393,7 +393,7 @@
activeViews.remove(it)
logger.logViewExpiration(it.info)
listeners.forEach { listener ->
- listener.onInfoPermanentlyRemoved(it.info.id)
+ listener.onInfoPermanentlyRemoved(it.info.id, REMOVAL_REASON_TIME_EXPIRED)
}
}
}
@@ -457,7 +457,7 @@
* Called whenever a [DisplayInfo] with the given [id] has been removed and will never be
* displayed again (unless another call to [updateView] is made).
*/
- fun onInfoPermanentlyRemoved(id: String)
+ fun onInfoPermanentlyRemoved(id: String, reason: String)
}
/** A container for all the display-related state objects. */
@@ -494,6 +494,7 @@
}
private const val REMOVAL_REASON_TIMEOUT = "TIMEOUT"
+private const val REMOVAL_REASON_TIME_EXPIRED = "TIMEOUT_EXPIRED_BEFORE_REDISPLAY"
private const val MIN_REQUIRED_TIME_FOR_REDISPLAY = 1000
private data class IconInfo(
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index 9e0bbb7..46f13cc 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -206,31 +206,44 @@
}
override fun animateViewIn(view: ViewGroup) {
- ViewHierarchyAnimator.animateAddition(
- view.getInnerView(),
- ViewHierarchyAnimator.Hotspot.TOP,
- Interpolators.EMPHASIZED_DECELERATE,
- duration = ANIMATION_IN_DURATION,
- includeMargins = true,
- includeFadeIn = true,
- // We can only request focus once the animation finishes.
- onAnimationEnd = {
- maybeGetAccessibilityFocus(view.getTag(INFO_TAG) as ChipbarInfo?, view)
- },
- )
+ val onAnimationEnd = Runnable {
+ maybeGetAccessibilityFocus(view.getTag(INFO_TAG) as ChipbarInfo?, view)
+ }
+ val added =
+ ViewHierarchyAnimator.animateAddition(
+ view.getInnerView(),
+ ViewHierarchyAnimator.Hotspot.TOP,
+ Interpolators.EMPHASIZED_DECELERATE,
+ duration = ANIMATION_IN_DURATION,
+ includeMargins = true,
+ includeFadeIn = true,
+ // We can only request focus once the animation finishes.
+ onAnimationEnd = onAnimationEnd,
+ )
+ // If the view doesn't get animated, the [onAnimationEnd] runnable won't get run. So, just
+ // run it immediately.
+ if (!added) {
+ onAnimationEnd.run()
+ }
}
override fun animateViewOut(view: ViewGroup, removalReason: String?, onAnimationEnd: Runnable) {
val innerView = view.getInnerView()
innerView.accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_NONE
- ViewHierarchyAnimator.animateRemoval(
- innerView,
- ViewHierarchyAnimator.Hotspot.TOP,
- Interpolators.EMPHASIZED_ACCELERATE,
- ANIMATION_OUT_DURATION,
- includeMargins = true,
- onAnimationEnd,
- )
+ val removed =
+ ViewHierarchyAnimator.animateRemoval(
+ innerView,
+ ViewHierarchyAnimator.Hotspot.TOP,
+ Interpolators.EMPHASIZED_ACCELERATE,
+ ANIMATION_OUT_DURATION,
+ includeMargins = true,
+ onAnimationEnd,
+ )
+ // If the view doesn't get animated, the [onAnimationEnd] runnable won't get run. So, just
+ // run it immediately.
+ if (!removed) {
+ onAnimationEnd.run()
+ }
updateGestureListening()
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 87dd6a4..eaef959 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -18,7 +18,6 @@
import static android.app.StatusBarManager.SESSION_KEYGUARD;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
-import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON;
@@ -42,7 +41,6 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -96,7 +94,6 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
-import android.provider.Settings;
import android.service.dreams.IDreamManager;
import android.service.trust.TrustAgentService;
import android.telephony.ServiceState;
@@ -239,8 +236,6 @@
@Mock
private UiEventLogger mUiEventLogger;
@Mock
- private PowerManager mPowerManager;
- @Mock
private GlobalSettings mGlobalSettings;
private FaceWakeUpTriggersConfig mFaceWakeUpTriggersConfig;
@Mock
@@ -834,6 +829,19 @@
}
@Test
+ public void doesNotTryToAuthenticateWhenKeyguardIsNotShowingButOccluded_whenAssistant() {
+ mKeyguardUpdateMonitor.setKeyguardShowing(false, true);
+ mKeyguardUpdateMonitor.setAssistantVisible(true);
+
+ verify(mFaceManager, never()).authenticate(any(),
+ any(),
+ any(),
+ any(),
+ anyInt(),
+ anyBoolean());
+ }
+
+ @Test
public void testTriesToAuthenticate_whenTrustOnAgentKeyguard_ifBypass() {
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
mTestableLooper.processAllMessages();
@@ -1872,28 +1880,6 @@
}
@Test
- public void testFingerAcquired_wakesUpPowerManager() {
- cleanupKeyguardUpdateMonitor();
- mContext.getOrCreateTestableResources().addOverride(
- com.android.internal.R.bool.kg_wake_on_acquire_start, true);
- mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mContext);
- fingerprintAcquireStart();
-
- verify(mPowerManager).wakeUp(anyLong(), anyInt(), anyString());
- }
-
- @Test
- public void testFingerAcquired_doesNotWakeUpPowerManager() {
- cleanupKeyguardUpdateMonitor();
- mContext.getOrCreateTestableResources().addOverride(
- com.android.internal.R.bool.kg_wake_on_acquire_start, false);
- mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mContext);
- fingerprintAcquireStart();
-
- verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
- }
-
- @Test
public void testDreamingStopped_faceDoesNotRun() {
mKeyguardUpdateMonitor.dispatchDreamingStopped();
mTestableLooper.processAllMessages();
@@ -2374,11 +2360,6 @@
.onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT, "Fingerprint locked out");
}
- private void fingerprintAcquireStart() {
- mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
- .onAuthenticationAcquired(FINGERPRINT_ACQUIRED_START);
- }
-
private void deviceInPostureStateOpened() {
mKeyguardUpdateMonitor.mPostureCallback.onPostureChanged(DEVICE_POSTURE_OPENED);
}
@@ -2525,7 +2506,7 @@
mAuthController, mTelephonyListenerManager,
mInteractionJankMonitor, mLatencyTracker, mActiveUnlockConfig,
mKeyguardUpdateMonitorLogger, mUiEventLogger, () -> mSessionTracker,
- mPowerManager, mTrustManager, mSubscriptionManager, mUserManager,
+ mTrustManager, mSubscriptionManager, mUserManager,
mDreamManager, mDevicePolicyManager, mSensorPrivacyManager, mTelephonyManager,
mPackageManager, mFaceManager, mFingerprintManager, mBiometricManager,
mFaceWakeUpTriggersConfig, mDevicePostureController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 022afdd..4b04b7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -234,7 +234,10 @@
@Test
fun `startButton - in preview mode - visible even when keyguard not showing`() =
testScope.runTest {
- underTest.enablePreviewMode(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START)
+ underTest.enablePreviewMode(
+ initiallySelectedSlotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+ shouldHighlightSelectedAffordance = true,
+ )
repository.setKeyguardShowing(false)
val latest = collectLastValue(underTest.startButton)
@@ -263,6 +266,7 @@
icon = icon,
canShowWhileLocked = false,
intent = Intent("action"),
+ isSelected = true,
),
configKey = configKey,
)
@@ -270,6 +274,60 @@
}
@Test
+ fun `endButton - in higlighted preview mode - dimmed when other is selected`() =
+ testScope.runTest {
+ underTest.enablePreviewMode(
+ initiallySelectedSlotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+ shouldHighlightSelectedAffordance = true,
+ )
+ repository.setKeyguardShowing(false)
+ val startButton = collectLastValue(underTest.startButton)
+ val endButton = collectLastValue(underTest.endButton)
+
+ val icon: Icon = mock()
+ setUpQuickAffordanceModel(
+ position = KeyguardQuickAffordancePosition.BOTTOM_START,
+ testConfig =
+ TestConfig(
+ isVisible = true,
+ isClickable = true,
+ isActivated = true,
+ icon = icon,
+ canShowWhileLocked = false,
+ intent = Intent("action"),
+ ),
+ )
+ val configKey =
+ setUpQuickAffordanceModel(
+ position = KeyguardQuickAffordancePosition.BOTTOM_END,
+ testConfig =
+ TestConfig(
+ isVisible = true,
+ isClickable = true,
+ isActivated = true,
+ icon = icon,
+ canShowWhileLocked = false,
+ intent = Intent("action"),
+ ),
+ )
+
+ assertQuickAffordanceViewModel(
+ viewModel = endButton(),
+ testConfig =
+ TestConfig(
+ isVisible = true,
+ isClickable = false,
+ isActivated = true,
+ icon = icon,
+ canShowWhileLocked = false,
+ intent = Intent("action"),
+ isDimmed = true,
+ ),
+ configKey = configKey,
+ )
+ }
+
+ @Test
fun `endButton - present - visible model - do nothing on click`() =
testScope.runTest {
repository.setKeyguardShowing(true)
@@ -377,7 +435,10 @@
@Test
fun `alpha - in preview mode - does not change`() =
testScope.runTest {
- underTest.enablePreviewMode(null)
+ underTest.enablePreviewMode(
+ initiallySelectedSlotId = null,
+ shouldHighlightSelectedAffordance = false,
+ )
val value = collectLastValue(underTest.alpha)
assertThat(value()).isEqualTo(1f)
@@ -639,6 +700,8 @@
assertThat(viewModel.isVisible).isEqualTo(testConfig.isVisible)
assertThat(viewModel.isClickable).isEqualTo(testConfig.isClickable)
assertThat(viewModel.isActivated).isEqualTo(testConfig.isActivated)
+ assertThat(viewModel.isSelected).isEqualTo(testConfig.isSelected)
+ assertThat(viewModel.isDimmed).isEqualTo(testConfig.isDimmed)
if (testConfig.isVisible) {
assertThat(viewModel.icon).isEqualTo(testConfig.icon)
viewModel.onClicked.invoke(
@@ -664,6 +727,8 @@
val icon: Icon? = null,
val canShowWhileLocked: Boolean = false,
val intent: Intent? = null,
+ val isSelected: Boolean = false,
+ val isDimmed: Boolean = false,
) {
init {
check(!isVisible || icon != null) { "Must supply non-null icon if visible!" }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtilsTest.kt
similarity index 70%
copy from packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
copy to packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtilsTest.kt
index 0e7bf8d..8da1c64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtilsTest.kt
@@ -22,7 +22,6 @@
import com.android.systemui.log.LogBufferFactory
import com.android.systemui.plugins.log.LogBuffer
import com.android.systemui.plugins.log.LogcatEchoTracker
-import com.android.systemui.temporarydisplay.TemporaryViewInfo
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.StringWriter
@@ -31,16 +30,15 @@
import org.mockito.Mockito.mock
@SmallTest
-class MediaTttLoggerTest : SysuiTestCase() {
+class MediaTttLoggerUtilsTest : SysuiTestCase() {
private lateinit var buffer: LogBuffer
- private lateinit var logger: MediaTttLogger<TemporaryViewInfo>
@Before
- fun setUp () {
- buffer = LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
- .create("buffer", 10)
- logger = MediaTttLogger(DEVICE_TYPE_TAG, buffer)
+ fun setUp() {
+ buffer =
+ LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
+ .create("buffer", 10)
}
@Test
@@ -49,35 +47,33 @@
val id = "test id"
val packageName = "this.is.a.package"
- logger.logStateChange(stateName, id, packageName)
+ MediaTttLoggerUtils.logStateChange(buffer, TAG, stateName, id, packageName)
val actualString = getStringFromBuffer()
- assertThat(actualString).contains(DEVICE_TYPE_TAG)
+ assertThat(actualString).contains(TAG)
assertThat(actualString).contains(stateName)
assertThat(actualString).contains(id)
assertThat(actualString).contains(packageName)
}
@Test
- fun logPackageNotFound_bufferHasPackageName() {
- val packageName = "this.is.a.package"
-
- logger.logPackageNotFound(packageName)
+ fun logStateChangeError_hasState() {
+ MediaTttLoggerUtils.logStateChangeError(buffer, TAG, 3456)
val actualString = getStringFromBuffer()
- assertThat(actualString).contains(packageName)
+ assertThat(actualString).contains(TAG)
+ assertThat(actualString).contains("3456")
}
@Test
- fun logRemovalBypass_bufferHasReasons() {
- val removalReason = "fakeRemovalReason"
- val bypassReason = "fakeBypassReason"
+ fun logPackageNotFound_bufferHasPackageName() {
+ val packageName = "this.is.a.package"
- logger.logRemovalBypass(removalReason, bypassReason)
+ MediaTttLoggerUtils.logPackageNotFound(buffer, TAG, packageName)
val actualString = getStringFromBuffer()
- assertThat(actualString).contains(removalReason)
- assertThat(actualString).contains(bypassReason)
+ assertThat(actualString).contains(TAG)
+ assertThat(actualString).contains(packageName)
}
private fun getStringFromBuffer(): String {
@@ -87,4 +83,4 @@
}
}
-private const val DEVICE_TYPE_TAG = "TEST TYPE"
+private const val TAG = "TEST TAG"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
index 561867f..8055b98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
@@ -25,7 +25,6 @@
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.temporarydisplay.TemporaryViewInfo
import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -41,7 +40,6 @@
private lateinit var appIconFromPackageName: Drawable
@Mock private lateinit var packageManager: PackageManager
@Mock private lateinit var applicationInfo: ApplicationInfo
- @Mock private lateinit var logger: MediaTttLogger<TemporaryViewInfo>
@Before
fun setUp() {
@@ -67,8 +65,7 @@
@Test
fun getIconInfoFromPackageName_nullPackageName_returnsDefault() {
- val iconInfo =
- MediaTttUtils.getIconInfoFromPackageName(context, appPackageName = null, logger)
+ val iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, appPackageName = null) {}
assertThat(iconInfo.isAppIcon).isFalse()
assertThat(iconInfo.contentDescription.loadContentDescription(context))
@@ -77,8 +74,19 @@
}
@Test
+ fun getIconInfoFromPackageName_nullPackageName_exceptionFnNotTriggered() {
+ var exceptionTriggered = false
+
+ MediaTttUtils.getIconInfoFromPackageName(context, appPackageName = null) {
+ exceptionTriggered = true
+ }
+
+ assertThat(exceptionTriggered).isFalse()
+ }
+
+ @Test
fun getIconInfoFromPackageName_invalidPackageName_returnsDefault() {
- val iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, "fakePackageName", logger)
+ val iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, "fakePackageName") {}
assertThat(iconInfo.isAppIcon).isFalse()
assertThat(iconInfo.contentDescription.loadContentDescription(context))
@@ -87,8 +95,19 @@
}
@Test
+ fun getIconInfoFromPackageName_invalidPackageName_exceptionFnTriggered() {
+ var exceptionTriggered = false
+
+ MediaTttUtils.getIconInfoFromPackageName(context, appPackageName = "fakePackageName") {
+ exceptionTriggered = true
+ }
+
+ assertThat(exceptionTriggered).isTrue()
+ }
+
+ @Test
fun getIconInfoFromPackageName_validPackageName_returnsAppInfo() {
- val iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, PACKAGE_NAME, logger)
+ val iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, PACKAGE_NAME) {}
assertThat(iconInfo.isAppIcon).isTrue()
assertThat(iconInfo.icon).isEqualTo(MediaTttIcon.Loaded(appIconFromPackageName))
@@ -96,6 +115,17 @@
}
@Test
+ fun getIconInfoFromPackageName_validPackageName_exceptionFnNotTriggered() {
+ var exceptionTriggered = false
+
+ MediaTttUtils.getIconInfoFromPackageName(context, PACKAGE_NAME) {
+ exceptionTriggered = true
+ }
+
+ assertThat(exceptionTriggered).isFalse()
+ }
+
+ @Test
fun iconInfo_toTintedIcon_loaded() {
val contentDescription = ContentDescription.Loaded("test")
val drawable = context.getDrawable(R.drawable.ic_cake)!!
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
index b3e621e..bd042c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
@@ -24,7 +24,6 @@
import android.view.accessibility.AccessibilityManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.taptotransfer.MediaTttFlags
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -35,7 +34,7 @@
class FakeMediaTttChipControllerReceiver(
commandQueue: CommandQueue,
context: Context,
- logger: MediaTttLogger<ChipReceiverInfo>,
+ logger: MediaTttReceiverLogger,
windowManager: WindowManager,
mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 5e40898..dba2da7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -36,7 +36,6 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.taptotransfer.MediaTttFlags
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.FakeExecutor
@@ -68,7 +67,7 @@
@Mock
private lateinit var applicationInfo: ApplicationInfo
@Mock
- private lateinit var logger: MediaTttLogger<ChipReceiverInfo>
+ private lateinit var logger: MediaTttReceiverLogger
@Mock
private lateinit var accessibilityManager: AccessibilityManager
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLoggerTest.kt
similarity index 71%
rename from packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLoggerTest.kt
index 0e7bf8d..95df484 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLoggerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.taptotransfer.common
+package com.android.systemui.media.taptotransfer.receiver
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -22,7 +22,6 @@
import com.android.systemui.log.LogBufferFactory
import com.android.systemui.plugins.log.LogBuffer
import com.android.systemui.plugins.log.LogcatEchoTracker
-import com.android.systemui.temporarydisplay.TemporaryViewInfo
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.StringWriter
@@ -31,16 +30,17 @@
import org.mockito.Mockito.mock
@SmallTest
-class MediaTttLoggerTest : SysuiTestCase() {
+class MediaTttReceiverLoggerTest : SysuiTestCase() {
private lateinit var buffer: LogBuffer
- private lateinit var logger: MediaTttLogger<TemporaryViewInfo>
+ private lateinit var logger: MediaTttReceiverLogger
@Before
- fun setUp () {
- buffer = LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
- .create("buffer", 10)
- logger = MediaTttLogger(DEVICE_TYPE_TAG, buffer)
+ fun setUp() {
+ buffer =
+ LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
+ .create("buffer", 10)
+ logger = MediaTttReceiverLogger(buffer)
}
@Test
@@ -52,13 +52,20 @@
logger.logStateChange(stateName, id, packageName)
val actualString = getStringFromBuffer()
- assertThat(actualString).contains(DEVICE_TYPE_TAG)
assertThat(actualString).contains(stateName)
assertThat(actualString).contains(id)
assertThat(actualString).contains(packageName)
}
@Test
+ fun logStateChangeError_hasState() {
+ logger.logStateChangeError(3456)
+
+ val actualString = getStringFromBuffer()
+ assertThat(actualString).contains("3456")
+ }
+
+ @Test
fun logPackageNotFound_bufferHasPackageName() {
val packageName = "this.is.a.package"
@@ -68,23 +75,9 @@
assertThat(actualString).contains(packageName)
}
- @Test
- fun logRemovalBypass_bufferHasReasons() {
- val removalReason = "fakeRemovalReason"
- val bypassReason = "fakeBypassReason"
-
- logger.logRemovalBypass(removalReason, bypassReason)
-
- val actualString = getStringFromBuffer()
- assertThat(actualString).contains(removalReason)
- assertThat(actualString).contains(bypassReason)
- }
-
private fun getStringFromBuffer(): String {
val stringWriter = StringWriter()
buffer.dump(PrintWriter(stringWriter), tailLength = 0)
return stringWriter.toString()
}
}
-
-private const val DEVICE_TYPE_TAG = "TEST TYPE"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
index 54d4460..c63ca3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
@@ -40,16 +40,13 @@
import com.android.systemui.common.shared.model.Text.Companion.loadText
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.taptotransfer.MediaTttFlags
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
-import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo
import com.android.systemui.temporarydisplay.chipbar.ChipbarLogger
-import com.android.systemui.temporarydisplay.chipbar.FakeChipbarCoordinator
import com.android.systemui.temporarydisplay.chipbar.SwipeChipbarAwayGestureHandler
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
@@ -92,7 +89,7 @@
@Mock private lateinit var falsingManager: FalsingManager
@Mock private lateinit var falsingCollector: FalsingCollector
@Mock private lateinit var chipbarLogger: ChipbarLogger
- @Mock private lateinit var logger: MediaTttLogger<ChipbarInfo>
+ @Mock private lateinit var logger: MediaTttSenderLogger
@Mock private lateinit var mediaTttFlags: MediaTttFlags
@Mock private lateinit var packageManager: PackageManager
@Mock private lateinit var powerManager: PowerManager
@@ -139,7 +136,7 @@
uiEventLogger = MediaTttSenderUiEventLogger(uiEventLoggerFake)
chipbarCoordinator =
- FakeChipbarCoordinator(
+ ChipbarCoordinator(
context,
chipbarLogger,
windowManager,
@@ -163,6 +160,7 @@
chipbarCoordinator,
commandQueue,
context,
+ dumpManager,
logger,
mediaTttFlags,
uiEventLogger,
@@ -181,6 +179,7 @@
chipbarCoordinator,
commandQueue,
context,
+ dumpManager,
logger,
mediaTttFlags,
uiEventLogger,
@@ -542,6 +541,7 @@
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
verify(windowManager).addView(viewCaptor.capture(), any())
verify(windowManager).removeView(viewCaptor.value)
+ verify(logger).logStateMapRemoval(eq(DEFAULT_ID), any())
}
@Test
@@ -741,6 +741,99 @@
verify(windowManager, never()).addView(any(), any())
}
+ /** Regression test for b/266217596. */
+ @Test
+ fun toReceiver_triggeredThenFar_thenSucceeded_updatesToSucceeded() {
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
+ routeInfo,
+ null,
+ )
+
+ // WHEN a FAR command comes in
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+ routeInfo,
+ null,
+ )
+
+ // THEN it is ignored and the chipbar is stilled displayed
+ val chipbarView = getChipbarView()
+ assertThat(chipbarView.getChipText())
+ .isEqualTo(ChipStateSender.TRANSFER_TO_RECEIVER_TRIGGERED.getExpectedStateText())
+ assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
+ verify(windowManager, never()).removeView(any())
+
+ // WHEN a SUCCEEDED command comes in
+ val succeededRouteInfo =
+ MediaRoute2Info.Builder(DEFAULT_ID, "Tablet Succeeded")
+ .addFeature("feature")
+ .setClientPackageName(PACKAGE_NAME)
+ .build()
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
+ succeededRouteInfo,
+ /* undoCallback= */ object : IUndoMediaTransferCallback.Stub() {
+ override fun onUndoTriggered() {}
+ },
+ )
+
+ // THEN it is *not* marked as an invalid transition and the chipbar updates to the succeeded
+ // state. (The "invalid transition" would be FAR => SUCCEEDED.)
+ assertThat(chipbarView.getChipText())
+ .isEqualTo(
+ ChipStateSender.TRANSFER_TO_RECEIVER_SUCCEEDED.getExpectedStateText(
+ "Tablet Succeeded"
+ )
+ )
+ assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.GONE)
+ assertThat(chipbarView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
+ }
+
+ /** Regression test for b/266217596. */
+ @Test
+ fun toThisDevice_triggeredThenFar_thenSucceeded_updatesToSucceeded() {
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
+ routeInfo,
+ null,
+ )
+
+ // WHEN a FAR command comes in
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+ routeInfo,
+ null,
+ )
+
+ // THEN it is ignored and the chipbar is stilled displayed
+ val chipbarView = getChipbarView()
+ assertThat(chipbarView.getChipText())
+ .isEqualTo(ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED.getExpectedStateText())
+ assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
+ verify(windowManager, never()).removeView(any())
+
+ // WHEN a SUCCEEDED command comes in
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
+ routeInfo,
+ /* undoCallback= */ object : IUndoMediaTransferCallback.Stub() {
+ override fun onUndoTriggered() {}
+ },
+ )
+
+ // THEN it is *not* marked as an invalid transition and the chipbar updates to the succeeded
+ // state. (The "invalid transition" would be FAR => SUCCEEDED.)
+ assertThat(chipbarView.getChipText())
+ .isEqualTo(
+ ChipStateSender.TRANSFER_TO_THIS_DEVICE_SUCCEEDED.getExpectedStateText(
+ "Tablet Succeeded"
+ )
+ )
+ assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.GONE)
+ assertThat(chipbarView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
+ }
+
@Test
fun receivesNewStateFromCommandQueue_isLogged() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
@@ -934,6 +1027,7 @@
mockChipbarCoordinator,
commandQueue,
context,
+ dumpManager,
logger,
mediaTttFlags,
uiEventLogger,
@@ -960,6 +1054,7 @@
mockChipbarCoordinator,
commandQueue,
context,
+ dumpManager,
logger,
mediaTttFlags,
uiEventLogger,
@@ -977,9 +1072,10 @@
verify(mockChipbarCoordinator).registerListener(capture(listenerCaptor))
// WHEN the listener is notified that the view has been removed
- listenerCaptor.value.onInfoPermanentlyRemoved(DEFAULT_ID)
+ listenerCaptor.value.onInfoPermanentlyRemoved(DEFAULT_ID, "reason")
// THEN the media coordinator unregisters the listener
+ verify(logger).logStateMapRemoval(DEFAULT_ID, "reason")
verify(mockChipbarCoordinator).unregisterListener(listenerCaptor.value)
}
@@ -991,6 +1087,7 @@
mockChipbarCoordinator,
commandQueue,
context,
+ dumpManager,
logger,
mediaTttFlags,
uiEventLogger,
@@ -1008,7 +1105,7 @@
verify(mockChipbarCoordinator).registerListener(capture(listenerCaptor))
// WHEN the listener is notified that a different view has been removed
- listenerCaptor.value.onInfoPermanentlyRemoved("differentViewId")
+ listenerCaptor.value.onInfoPermanentlyRemoved("differentViewId", "reason")
// THEN the media coordinator doesn't unregister the listener
verify(mockChipbarCoordinator, never()).unregisterListener(listenerCaptor.value)
@@ -1022,6 +1119,7 @@
mockChipbarCoordinator,
commandQueue,
context,
+ dumpManager,
logger,
mediaTttFlags,
uiEventLogger,
@@ -1057,6 +1155,7 @@
mockChipbarCoordinator,
commandQueue,
context,
+ dumpManager,
logger,
mediaTttFlags,
uiEventLogger,
@@ -1086,10 +1185,173 @@
verify(mockChipbarCoordinator, atLeast(1)).registerListener(capture(listenerCaptor))
// THEN one of them is removed
- listenerCaptor.value.onInfoPermanentlyRemoved("route1")
+ listenerCaptor.value.onInfoPermanentlyRemoved("route1", "reason")
// THEN the media coordinator doesn't unregister the listener (since route2 is still active)
verify(mockChipbarCoordinator, never()).unregisterListener(listenerCaptor.value)
+ verify(logger).logStateMapRemoval("route1", "reason")
+ }
+
+ /** Regression test for b/266218672. */
+ @Test
+ fun twoIdsDisplayed_oldIdIsFar_viewStillDisplayed() {
+ // WHEN there are two different media transfers with different IDs
+ val route1 =
+ MediaRoute2Info.Builder("route1", OTHER_DEVICE_NAME)
+ .addFeature("feature")
+ .setClientPackageName(PACKAGE_NAME)
+ .build()
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
+ route1,
+ null,
+ )
+ verify(windowManager).addView(any(), any())
+ reset(windowManager)
+
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
+ MediaRoute2Info.Builder("route2", "Route 2 name")
+ .addFeature("feature")
+ .setClientPackageName(PACKAGE_NAME)
+ .build(),
+ null,
+ )
+ val newView = getChipbarView()
+
+ // WHEN there's a FAR event for the earlier one
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+ route1,
+ null,
+ )
+
+ // THEN it's ignored and the more recent one is still displayed
+ assertThat(newView.getChipText())
+ .isEqualTo(
+ ChipStateSender.ALMOST_CLOSE_TO_START_CAST.getExpectedStateText("Route 2 name")
+ )
+ }
+
+ /** Regression test for b/266218672. */
+ @Test
+ fun receiverSucceededThenTimedOut_internalStateResetAndCanDisplayAlmostCloseToEnd() {
+ displayReceiverTriggered()
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
+ routeInfo,
+ null,
+ )
+
+ fakeClock.advanceTime(TIMEOUT + 1L)
+ verify(windowManager).removeView(any())
+
+ reset(windowManager)
+
+ // WHEN we try to show ALMOST_CLOSE_TO_END
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
+ routeInfo,
+ null,
+ )
+
+ // THEN it succeeds
+ val chipbarView = getChipbarView()
+ assertThat(chipbarView.getChipText())
+ .isEqualTo(ChipStateSender.ALMOST_CLOSE_TO_END_CAST.getExpectedStateText())
+ }
+
+ /** Regression test for b/266218672. */
+ @Test
+ fun receiverSucceededThenTimedOut_internalStateResetAndCanDisplayReceiverTriggered() {
+ displayReceiverTriggered()
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
+ routeInfo,
+ null,
+ )
+
+ fakeClock.advanceTime(TIMEOUT + 1L)
+ verify(windowManager).removeView(any())
+
+ reset(windowManager)
+
+ // WHEN we try to show RECEIVER_TRIGGERED
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
+ routeInfo,
+ null,
+ )
+
+ // THEN it succeeds
+ val chipbarView = getChipbarView()
+ assertThat(chipbarView.getChipText())
+ .isEqualTo(ChipStateSender.TRANSFER_TO_RECEIVER_TRIGGERED.getExpectedStateText())
+ assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
+ }
+
+ /** Regression test for b/266218672. */
+ @Test
+ fun toThisDeviceSucceededThenTimedOut_internalStateResetAndCanDisplayAlmostCloseToStart() {
+ displayThisDeviceTriggered()
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
+ routeInfo,
+ null,
+ )
+
+ fakeClock.advanceTime(TIMEOUT + 1L)
+ verify(windowManager).removeView(any())
+
+ reset(windowManager)
+
+ // WHEN we try to show ALMOST_CLOSE_TO_START
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
+ routeInfo,
+ null,
+ )
+
+ // THEN it succeeds
+ val chipbarView = getChipbarView()
+ assertThat(chipbarView.getChipText())
+ .isEqualTo(ChipStateSender.ALMOST_CLOSE_TO_START_CAST.getExpectedStateText())
+ }
+
+ /** Regression test for b/266218672. */
+ @Test
+ fun toThisDeviceSucceededThenTimedOut_internalStateResetAndCanDisplayThisDeviceTriggered() {
+ displayThisDeviceTriggered()
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
+ routeInfo,
+ null,
+ )
+
+ fakeClock.advanceTime(TIMEOUT + 1L)
+ verify(windowManager).removeView(any())
+
+ reset(windowManager)
+
+ // WHEN we try to show THIS_DEVICE_TRIGGERED
+ val newRouteInfo =
+ MediaRoute2Info.Builder(DEFAULT_ID, "New Name")
+ .addFeature("feature")
+ .setClientPackageName(PACKAGE_NAME)
+ .build()
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
+ newRouteInfo,
+ null,
+ )
+
+ // THEN it succeeds
+ val chipbarView = getChipbarView()
+ assertThat(chipbarView.getChipText())
+ .isEqualTo(
+ ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED.getExpectedStateText("New Name")
+ )
+ assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
}
private fun getChipbarView(): ViewGroup {
@@ -1109,8 +1371,10 @@
private fun ViewGroup.getUndoButton(): View = this.requireViewById(R.id.end_button)
- private fun ChipStateSender.getExpectedStateText(): String? {
- return this.getChipTextString(context, OTHER_DEVICE_NAME).loadText(context)
+ private fun ChipStateSender.getExpectedStateText(
+ otherDeviceName: String = OTHER_DEVICE_NAME,
+ ): String? {
+ return this.getChipTextString(context, otherDeviceName).loadText(context)
}
// display receiver triggered state helper method to make sure we start from a valid state
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLoggerTest.kt
similarity index 62%
copy from packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
copy to packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLoggerTest.kt
index 0e7bf8d..0033757 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLoggerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.taptotransfer.common
+package com.android.systemui.media.taptotransfer.sender
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -22,7 +22,6 @@
import com.android.systemui.log.LogBufferFactory
import com.android.systemui.plugins.log.LogBuffer
import com.android.systemui.plugins.log.LogcatEchoTracker
-import com.android.systemui.temporarydisplay.TemporaryViewInfo
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.StringWriter
@@ -31,16 +30,17 @@
import org.mockito.Mockito.mock
@SmallTest
-class MediaTttLoggerTest : SysuiTestCase() {
+class MediaTttSenderLoggerTest : SysuiTestCase() {
private lateinit var buffer: LogBuffer
- private lateinit var logger: MediaTttLogger<TemporaryViewInfo>
+ private lateinit var logger: MediaTttSenderLogger
@Before
- fun setUp () {
- buffer = LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
- .create("buffer", 10)
- logger = MediaTttLogger(DEVICE_TYPE_TAG, buffer)
+ fun setUp() {
+ buffer =
+ LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
+ .create("buffer", 10)
+ logger = MediaTttSenderLogger(buffer)
}
@Test
@@ -52,13 +52,20 @@
logger.logStateChange(stateName, id, packageName)
val actualString = getStringFromBuffer()
- assertThat(actualString).contains(DEVICE_TYPE_TAG)
assertThat(actualString).contains(stateName)
assertThat(actualString).contains(id)
assertThat(actualString).contains(packageName)
}
@Test
+ fun logStateChangeError_hasState() {
+ logger.logStateChangeError(3456)
+
+ val actualString = getStringFromBuffer()
+ assertThat(actualString).contains("3456")
+ }
+
+ @Test
fun logPackageNotFound_bufferHasPackageName() {
val packageName = "this.is.a.package"
@@ -80,11 +87,35 @@
assertThat(actualString).contains(bypassReason)
}
+ @Test
+ fun logStateMap_bufferHasInfo() {
+ val map =
+ mapOf(
+ "123" to ChipStateSender.ALMOST_CLOSE_TO_START_CAST,
+ "456" to ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED,
+ )
+
+ logger.logStateMap(map)
+
+ val actualString = getStringFromBuffer()
+ assertThat(actualString).contains("123")
+ assertThat(actualString).contains(ChipStateSender.ALMOST_CLOSE_TO_START_CAST.name)
+ assertThat(actualString).contains("456")
+ assertThat(actualString).contains(ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED.name)
+ }
+
+ @Test
+ fun logStateMapRemoval_bufferHasInfo() {
+ logger.logStateMapRemoval("456", "testReason")
+
+ val actualString = getStringFromBuffer()
+ assertThat(actualString).contains("456")
+ assertThat(actualString).contains("testReason")
+ }
+
private fun getStringFromBuffer(): String {
val stringWriter = StringWriter()
buffer.dump(PrintWriter(stringWriter), tailLength = 0)
return stringWriter.toString()
}
}
-
-private const val DEVICE_TYPE_TAG = "TEST TYPE"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
index 43fb1bd..dee1cc8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
@@ -59,6 +59,7 @@
@SmallTest
public class AutoAddTrackerTest extends SysuiTestCase {
+ private static final int END_POSITION = -1;
private static final int USER = 0;
@Mock
@@ -142,6 +143,29 @@
}
@Test
+ public void testRestoredTilePositionPreserved() {
+ verify(mBroadcastDispatcher).registerReceiver(
+ mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any(), anyInt(), any());
+ String restoredTiles = "saver,internet,work,cast";
+ Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, restoredTiles);
+
+ mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
+
+ assertEquals(2, mAutoTracker.getRestoredTilePosition("work"));
+ }
+
+ @Test
+ public void testNoRestoredTileReturnsEndPosition() {
+ verify(mBroadcastDispatcher).registerReceiver(
+ mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any(), anyInt(), any());
+ Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, null);
+
+ mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
+
+ assertEquals(END_POSITION, mAutoTracker.getRestoredTilePosition("work"));
+ }
+
+ @Test
public void testBroadcastReceiverRegistered() {
verify(mBroadcastDispatcher).registerReceiver(
any(), mIntentFilterArgumentCaptor.capture(), any(), eq(UserHandle.of(USER)),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
index ed9baf5b1..3706859 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
@@ -90,7 +90,7 @@
fun testEdgeElementsAlignedWithEdgeOrGuide_qs() {
with(qsConstraint) {
assertThat(getConstraint(R.id.clock).layout.startToStart).isEqualTo(PARENT_ID)
- assertThat(getConstraint(R.id.clock).layout.horizontalBias).isEqualTo(0f)
+ assertThat(getConstraint(R.id.clock).layout.horizontalBias).isEqualTo(0.5f)
assertThat(getConstraint(R.id.date).layout.startToStart).isEqualTo(PARENT_ID)
assertThat(getConstraint(R.id.date).layout.horizontalBias).isEqualTo(0.5f)
@@ -342,7 +342,6 @@
R.id.clock to "clock",
R.id.date to "date",
R.id.privacy_container to "privacy",
- R.id.carrier_group to "carriers",
)
views.forEach { (id, name) ->
assertWithMessage("$name has 0 height in qqs")
@@ -361,7 +360,6 @@
val views = mapOf(
R.id.clock to "clock",
R.id.privacy_container to "privacy",
- R.id.carrier_group to "carriers",
)
views.forEach { (id, name) ->
expect.withMessage("$name changes height")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
index f580f5e..91fef1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
@@ -692,6 +692,19 @@
assertThat(captor.value.getResId()).isEqualTo(R.xml.large_screen_shade_header)
}
+ @Test
+ fun `carrier left padding is set when clock layout changes`() {
+ val width = 200
+ whenever(clock.width).thenReturn(width)
+ whenever(clock.scaleX).thenReturn(2.57f) // 2.57 comes from qs_header.xml
+ val captor = ArgumentCaptor.forClass(View.OnLayoutChangeListener::class.java)
+
+ verify(clock).addOnLayoutChangeListener(capture(captor))
+ captor.value.onLayoutChange(clock, 0, 0, width, 0, 0, 0, 0, 0)
+
+ verify(carrierGroup).setPaddingRelative(514, 0, 0, 0)
+ }
+
private fun View.executeLayoutChange(
left: Int,
top: Int,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
index b568122..2bf2a81 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
@@ -1,5 +1,6 @@
package com.android.systemui.shade
+import android.animation.Animator
import android.animation.ValueAnimator
import android.app.StatusBarManager
import android.content.Context
@@ -45,8 +46,8 @@
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -293,6 +294,34 @@
}
@Test
+ fun animatorListenersClearedAtEnd() {
+ val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
+ whenever(view.animate()).thenReturn(animator)
+
+ mLargeScreenShadeHeaderController.startCustomizingAnimation(show = true, 0L)
+ val listenerCaptor = argumentCaptor<Animator.AnimatorListener>()
+ verify(animator).setListener(capture(listenerCaptor))
+
+ listenerCaptor.value.onAnimationEnd(mock())
+ verify(animator).setListener(null)
+ verify(animator).setUpdateListener(null)
+ }
+
+ @Test
+ fun animatorListenersClearedOnCancel() {
+ val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
+ whenever(view.animate()).thenReturn(animator)
+
+ mLargeScreenShadeHeaderController.startCustomizingAnimation(show = true, 0L)
+ val listenerCaptor = argumentCaptor<Animator.AnimatorListener>()
+ verify(animator).setListener(capture(listenerCaptor))
+
+ listenerCaptor.value.onAnimationCancel(mock())
+ verify(animator).setListener(null)
+ verify(animator).setUpdateListener(null)
+ }
+
+ @Test
fun demoMode_attachDemoMode() {
val cb = argumentCaptor<DemoMode>()
verify(demoModeController).addCallback(capture(cb))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index b6a1bb3..d7ac6b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -66,9 +66,9 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -133,18 +133,13 @@
@Mock private ShadeController mShadeController;
@Mock private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
@Mock private AssistantFeedbackController mAssistantFeedbackController;
+ @Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
+ @Mock private StatusBarStateController mStatusBarStateController;
@Before
public void setUp() {
mTestableLooper = TestableLooper.get(this);
allowTestableLooperAsMainThread();
- mDependency.injectTestDependency(DeviceProvisionedController.class,
- mDeviceProvisionedController);
- mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
- mDependency.injectTestDependency(
- OnUserInteractionCallback.class,
- mOnUserInteractionCallback);
- mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
mHandler = Handler.createAsync(mTestableLooper.getLooper());
mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this));
when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
@@ -155,7 +150,11 @@
mPeopleSpaceWidgetManager, mLauncherApps, mShortcutManager,
mChannelEditorDialogController, mContextTracker, mAssistantFeedbackController,
Optional.of(mBubblesManager), new UiEventLoggerFake(), mOnUserInteractionCallback,
- mShadeController);
+ mShadeController,
+ mNotificationLockscreenUserManager,
+ mStatusBarStateController,
+ mDeviceProvisionedController,
+ mMetricsLogger);
mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer,
mOnSettingsClickListener);
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
@@ -372,7 +371,8 @@
eq(false),
eq(false),
eq(true), /* wasShownHighPriority */
- eq(mAssistantFeedbackController));
+ eq(mAssistantFeedbackController),
+ any(MetricsLogger.class));
}
@Test
@@ -406,7 +406,8 @@
eq(true),
eq(false),
eq(false), /* wasShownHighPriority */
- eq(mAssistantFeedbackController));
+ eq(mAssistantFeedbackController),
+ any(MetricsLogger.class));
}
@Test
@@ -438,7 +439,8 @@
eq(false),
eq(false),
eq(false), /* wasShownHighPriority */
- eq(mAssistantFeedbackController));
+ eq(mAssistantFeedbackController),
+ any(MetricsLogger.class));
}
////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 80a81a5..8dd0488 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -130,7 +130,6 @@
mContext.addMockSystemService(TelecomManager.class, mTelecomManager);
mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
- mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
// Inflate the layout
final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
mNotificationInfo = (NotificationInfo) layoutInflater.inflate(R.layout.notification_info,
@@ -194,7 +193,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final TextView textView = mNotificationInfo.findViewById(R.id.pkg_name);
assertTrue(textView.getText().toString().contains("App Name"));
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -220,7 +220,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final ImageView iconView = mNotificationInfo.findViewById(R.id.pkg_icon);
assertEquals(iconDrawable, iconView.getDrawable());
}
@@ -242,7 +243,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(GONE, nameView.getVisibility());
}
@@ -273,7 +275,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(VISIBLE, nameView.getVisibility());
assertTrue(nameView.getText().toString().contains("Proxied"));
@@ -296,7 +299,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(GONE, groupNameView.getVisibility());
}
@@ -324,7 +328,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(View.VISIBLE, groupNameView.getVisibility());
assertEquals("Test Group Name", groupNameView.getText());
@@ -347,7 +352,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(TEST_CHANNEL_NAME, textView.getText());
}
@@ -369,7 +375,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(GONE, textView.getVisibility());
}
@@ -395,7 +402,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(VISIBLE, textView.getVisibility());
}
@@ -417,7 +425,8 @@
true,
true,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(VISIBLE, textView.getVisibility());
}
@@ -443,7 +452,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
settingsButton.performClick();
@@ -468,7 +478,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -493,7 +504,8 @@
false,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -515,7 +527,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.bindNotification(
mMockPackageManager,
mMockINotificationManager,
@@ -531,7 +544,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertEquals(View.VISIBLE, settingsButton.getVisibility());
}
@@ -556,7 +570,8 @@
true,
true,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.info).performClick();
// Verify that listener was triggered.
@@ -582,7 +597,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final TextView channelNameView =
mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(GONE, channelNameView.getVisibility());
@@ -606,7 +622,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
assertEquals(GONE, mNotificationInfo.findViewById(
R.id.interruptiveness_settings).getVisibility());
assertEquals(VISIBLE, mNotificationInfo.findViewById(
@@ -630,7 +647,8 @@
true,
true,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final TextView view = mNotificationInfo.findViewById(R.id.non_configurable_text);
assertEquals(View.VISIBLE, view.getVisibility());
assertEquals(mContext.getString(R.string.notification_unblockable_desc),
@@ -673,7 +691,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
final TextView view = mNotificationInfo.findViewById(R.id.non_configurable_call_text);
assertEquals(View.VISIBLE, view.getVisibility());
assertEquals(mContext.getString(R.string.notification_unblockable_call_desc),
@@ -716,7 +735,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
assertEquals(GONE,
mNotificationInfo.findViewById(R.id.non_configurable_call_text).getVisibility());
assertEquals(VISIBLE,
@@ -743,7 +763,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.automatic).getVisibility());
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.automatic_summary).getVisibility());
}
@@ -765,7 +786,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
assertEquals(GONE, mNotificationInfo.findViewById(R.id.automatic).getVisibility());
assertEquals(GONE, mNotificationInfo.findViewById(R.id.automatic_summary).getVisibility());
}
@@ -789,7 +811,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
assertTrue(mNotificationInfo.findViewById(R.id.automatic).isSelected());
}
@@ -810,7 +833,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
assertTrue(mNotificationInfo.findViewById(R.id.alert).isSelected());
}
@@ -831,7 +855,8 @@
true,
false,
false,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
assertTrue(mNotificationInfo.findViewById(R.id.silence).isSelected());
}
@@ -852,7 +877,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mTestableLooper.processAllMessages();
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), eq(TEST_UID), any());
@@ -875,7 +901,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
assertEquals(1, mUiEventLogger.numLogs());
assertEquals(NotificationControlsEvent.NOTIFICATION_CONTROLS_OPEN.getId(),
mUiEventLogger.eventId(0));
@@ -899,7 +926,8 @@
true,
false,
false,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.alert).performClick();
mTestableLooper.processAllMessages();
@@ -926,7 +954,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.silence).performClick();
mTestableLooper.processAllMessages();
@@ -953,7 +982,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.automatic).performClick();
mTestableLooper.processAllMessages();
@@ -981,7 +1011,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.handleCloseControls(true, false);
mTestableLooper.processAllMessages();
@@ -1008,7 +1039,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.handleCloseControls(true, false);
mTestableLooper.processAllMessages();
@@ -1043,7 +1075,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.handleCloseControls(true, false);
@@ -1071,7 +1104,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.silence).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1112,7 +1146,8 @@
true,
false,
false,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.alert).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1149,7 +1184,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.automatic).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1181,7 +1217,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.silence).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1217,7 +1254,8 @@
true,
false,
false,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
assertEquals(mContext.getString(R.string.inline_done_button),
((TextView) mNotificationInfo.findViewById(R.id.done)).getText());
@@ -1255,7 +1293,8 @@
true,
false,
false,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.silence).performClick();
mNotificationInfo.handleCloseControls(false, false);
@@ -1286,7 +1325,8 @@
true,
false,
false,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
assertEquals(mContext.getString(R.string.inline_done_button),
((TextView) mNotificationInfo.findViewById(R.id.done)).getText());
@@ -1324,7 +1364,8 @@
true,
false,
true,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.silence).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1353,7 +1394,8 @@
true,
false,
false,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
assertEquals(mContext.getString(R.string.inline_done_button),
((TextView) mNotificationInfo.findViewById(R.id.done)).getText());
@@ -1384,7 +1426,8 @@
true,
false,
false,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.alert).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1419,7 +1462,8 @@
true,
false,
false,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.alert).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1452,7 +1496,8 @@
true,
false,
false,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.alert).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -1485,7 +1530,8 @@
true,
false,
false,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
mNotificationInfo.findViewById(R.id.alert).performClick();
@@ -1511,7 +1557,8 @@
true,
false,
false,
- mAssistantFeedbackController);
+ mAssistantFeedbackController,
+ mMetricsLogger);
assertFalse(mNotificationInfo.willBeRemoved());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 091bb54..8cfcc07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -510,6 +510,7 @@
@Test
public void managedProfileAdded_tileAdded() {
when(mAutoAddTracker.isAdded(eq("work"))).thenReturn(false);
+ when(mAutoAddTracker.getRestoredTilePosition(eq("work"))).thenReturn(2);
mAutoTileManager = createAutoTileManager(mContext);
Mockito.doAnswer((Answer<Object>) invocation -> {
mManagedProfileCallback = invocation.getArgument(0);
@@ -520,7 +521,7 @@
mManagedProfileCallback.onManagedProfileChanged();
- verify(mQsTileHost, times(1)).addTile(eq("work"));
+ verify(mQsTileHost, times(1)).addTile(eq("work"), eq(2));
verify(mAutoAddTracker, times(1)).setTileAdded(eq("work"));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index ae390a0..db8172a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -22,6 +22,7 @@
import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
+import android.os.ParcelUuid
import android.provider.Settings
import android.telephony.CarrierConfigManager
import android.telephony.SubscriptionInfo
@@ -50,6 +51,7 @@
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
+import java.util.UUID
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -104,6 +106,17 @@
mock<TableLogBuffer>()
}
+ // For convenience, set up the subscription info callbacks
+ whenever(subscriptionManager.getActiveSubscriptionInfo(anyInt())).thenAnswer { invocation ->
+ when (invocation.getArgument(0) as Int) {
+ 1 -> SUB_1
+ 2 -> SUB_2
+ 3 -> SUB_3
+ 4 -> SUB_4
+ else -> null
+ }
+ }
+
wifiRepository = FakeWifiRepository()
connectionFactory =
@@ -686,6 +699,38 @@
job.cancel()
}
+ @Test
+ fun `active data change - in same group - emits unit`() =
+ runBlocking(IMMEDIATE) {
+ var latest: Unit? = null
+ val job = underTest.activeSubChangedInGroupEvent.onEach { latest = it }.launchIn(this)
+
+ getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+ .onActiveDataSubscriptionIdChanged(SUB_3_ID_GROUPED)
+ getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+ .onActiveDataSubscriptionIdChanged(SUB_4_ID_GROUPED)
+
+ assertThat(latest).isEqualTo(Unit)
+
+ job.cancel()
+ }
+
+ @Test
+ fun `active data change - not in same group - does not emit`() =
+ runBlocking(IMMEDIATE) {
+ var latest: Unit? = null
+ val job = underTest.activeSubChangedInGroupEvent.onEach { latest = it }.launchIn(this)
+
+ getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+ .onActiveDataSubscriptionIdChanged(SUB_3_ID_GROUPED)
+ getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+ .onActiveDataSubscriptionIdChanged(SUB_1_ID)
+
+ assertThat(latest).isEqualTo(null)
+
+ job.cancel()
+ }
+
private fun createCapabilities(connected: Boolean, validated: Boolean): NetworkCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(connected)
@@ -719,19 +764,50 @@
companion object {
private val IMMEDIATE = Dispatchers.Main.immediate
+
+ // Subscription 1
private const val SUB_1_ID = 1
private val SUB_1 =
- mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_1_ID) }
+ mock<SubscriptionInfo>().also {
+ whenever(it.subscriptionId).thenReturn(SUB_1_ID)
+ whenever(it.groupUuid).thenReturn(ParcelUuid(UUID.randomUUID()))
+ }
private val MODEL_1 = SubscriptionModel(subscriptionId = SUB_1_ID)
+ // Subscription 2
private const val SUB_2_ID = 2
private val SUB_2 =
- mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_2_ID) }
+ mock<SubscriptionInfo>().also {
+ whenever(it.subscriptionId).thenReturn(SUB_2_ID)
+ whenever(it.groupUuid).thenReturn(ParcelUuid(UUID.randomUUID()))
+ }
private val MODEL_2 = SubscriptionModel(subscriptionId = SUB_2_ID)
+ // Subs 3 and 4 are considered to be in the same group ------------------------------------
+ private val GROUP_ID_3_4 = ParcelUuid(UUID.randomUUID())
+
+ // Subscription 3
+ private const val SUB_3_ID_GROUPED = 3
+ private val SUB_3 =
+ mock<SubscriptionInfo>().also {
+ whenever(it.subscriptionId).thenReturn(SUB_3_ID_GROUPED)
+ whenever(it.groupUuid).thenReturn(GROUP_ID_3_4)
+ }
+
+ // Subscription 4
+ private const val SUB_4_ID_GROUPED = 4
+ private val SUB_4 =
+ mock<SubscriptionInfo>().also {
+ whenever(it.subscriptionId).thenReturn(SUB_4_ID_GROUPED)
+ whenever(it.groupUuid).thenReturn(GROUP_ID_3_4)
+ }
+
+ // Subs 3 and 4 are considered to be in the same group ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
private const val NET_ID = 123
private val NETWORK = mock<Network>().apply { whenever(getNetId()).thenReturn(NET_ID) }
+ // Carrier merged subscription
private const val SUB_CM_ID = 5
private val SUB_CM =
mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_CM_ID) }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
index 45f7df3..c7c6b94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
@@ -1172,7 +1172,7 @@
inner class Listener : TemporaryViewDisplayController.Listener {
val permanentlyRemovedIds = mutableListOf<String>()
- override fun onInfoPermanentlyRemoved(id: String) {
+ override fun onInfoPermanentlyRemoved(id: String, reason: String) {
permanentlyRemovedIds.add(id)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
index 45eb1f9..dd04ac4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
@@ -66,7 +66,7 @@
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class ChipbarCoordinatorTest : SysuiTestCase() {
- private lateinit var underTest: FakeChipbarCoordinator
+ private lateinit var underTest: ChipbarCoordinator
@Mock private lateinit var logger: ChipbarLogger
@Mock private lateinit var accessibilityManager: AccessibilityManager
@@ -100,7 +100,7 @@
uiEventLoggerFake = UiEventLoggerFake()
underTest =
- FakeChipbarCoordinator(
+ ChipbarCoordinator(
context,
logger,
windowManager,
@@ -436,6 +436,23 @@
verify(logger).logViewUpdate(eq(WINDOW_TITLE), eq("new title text"), any())
}
+ /** Regression test for b/266209420. */
+ @Test
+ fun displayViewThenImmediateRemoval_viewStillRemoved() {
+ underTest.displayView(
+ createChipbarInfo(
+ Icon.Resource(R.drawable.ic_cake, contentDescription = null),
+ Text.Loaded("title text"),
+ endItem = ChipbarEndItem.Error,
+ ),
+ )
+ val chipbarView = getChipbarView()
+
+ underTest.removeView(DEVICE_ID, "test reason")
+
+ verify(windowManager).removeView(chipbarView)
+ }
+
@Test
fun swipeToDismiss_false_neverListensForGesture() {
underTest.displayView(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
deleted file mode 100644
index ffac8f6..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.temporarydisplay.chipbar
-
-import android.content.Context
-import android.os.PowerManager
-import android.view.ViewGroup
-import android.view.WindowManager
-import android.view.accessibility.AccessibilityManager
-import com.android.systemui.classifier.FalsingCollector
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.statusbar.VibratorHelper
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.time.SystemClock
-import com.android.systemui.util.view.ViewUtil
-import com.android.systemui.util.wakelock.WakeLock
-
-/** A fake implementation of [ChipbarCoordinator] for testing. */
-class FakeChipbarCoordinator(
- context: Context,
- logger: ChipbarLogger,
- windowManager: WindowManager,
- mainExecutor: DelayableExecutor,
- accessibilityManager: AccessibilityManager,
- configurationController: ConfigurationController,
- dumpManager: DumpManager,
- powerManager: PowerManager,
- falsingManager: FalsingManager,
- falsingCollector: FalsingCollector,
- swipeChipbarAwayGestureHandler: SwipeChipbarAwayGestureHandler,
- viewUtil: ViewUtil,
- vibratorHelper: VibratorHelper,
- wakeLockBuilder: WakeLock.Builder,
- systemClock: SystemClock,
-) :
- ChipbarCoordinator(
- context,
- logger,
- windowManager,
- mainExecutor,
- accessibilityManager,
- configurationController,
- dumpManager,
- powerManager,
- falsingManager,
- falsingCollector,
- swipeChipbarAwayGestureHandler,
- viewUtil,
- vibratorHelper,
- wakeLockBuilder,
- systemClock,
- ) {
- override fun animateViewOut(view: ViewGroup, removalReason: String?, onAnimationEnd: Runnable) {
- // Just bypass the animation in tests
- onAnimationEnd.run()
- }
-}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java
index 7ea1e6c..97e70a4 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java
@@ -16,60 +16,148 @@
package com.android.server.companion.datatransfer.contextsync;
+import android.annotation.Nullable;
import android.telecom.Call;
import android.telecom.InCallService;
+import android.telecom.TelecomManager;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.companion.CompanionDeviceConfig;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicLong;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
-/** In-call service to sync call metadata across a user's devices. */
+/**
+ * In-call service to sync call metadata across a user's devices. Note that mute and silence are
+ * global states and apply to all current calls.
+ */
public class CallMetadataSyncInCallService extends InCallService {
+ private static final long NOT_VALID = -1L;
+
@VisibleForTesting
- final Set<CrossDeviceCall> mCurrentCalls = new HashSet<>();
+ final Map<Call, CrossDeviceCall> mCurrentCalls = new HashMap<>();
+ final Call.Callback mTelecomCallback = new Call.Callback() {
+ @Override
+ public void onDetailsChanged(Call call, Call.Details details) {
+ mCurrentCalls.get(call).updateCallDetails(details);
+ }
+ };
+ final CallMetadataSyncCallback mCallMetadataSyncCallback = new CallMetadataSyncCallback() {
+ @Override
+ void processCallControlAction(int crossDeviceCallId, int callControlAction) {
+ final CrossDeviceCall crossDeviceCall = getCallForId(crossDeviceCallId,
+ mCurrentCalls.values());
+ switch (callControlAction) {
+ case android.companion.Telecom.Call.ACCEPT:
+ if (crossDeviceCall != null) {
+ crossDeviceCall.doAccept();
+ }
+ break;
+ case android.companion.Telecom.Call.REJECT:
+ if (crossDeviceCall != null) {
+ crossDeviceCall.doReject();
+ }
+ break;
+ case android.companion.Telecom.Call.SILENCE:
+ doSilence();
+ break;
+ case android.companion.Telecom.Call.MUTE:
+ doMute();
+ break;
+ case android.companion.Telecom.Call.UNMUTE:
+ doUnmute();
+ break;
+ case android.companion.Telecom.Call.END:
+ if (crossDeviceCall != null) {
+ crossDeviceCall.doEnd();
+ }
+ break;
+ case android.companion.Telecom.Call.PUT_ON_HOLD:
+ if (crossDeviceCall != null) {
+ crossDeviceCall.doPutOnHold();
+ }
+ break;
+ case android.companion.Telecom.Call.TAKE_OFF_HOLD:
+ if (crossDeviceCall != null) {
+ crossDeviceCall.doTakeOffHold();
+ }
+ break;
+ default:
+ }
+ }
+
+ @Override
+ void requestCrossDeviceSync(int userId) {
+ }
+ };
@Override
public void onCreate() {
super.onCreate();
- mCurrentCalls.addAll(getCalls().stream().map(CrossDeviceCall::new).toList());
+ if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
+ mCurrentCalls.putAll(getCalls().stream().collect(Collectors.toMap(call -> call,
+ call -> new CrossDeviceCall(getPackageManager(), call, getCallAudioState()))));
+ }
+ }
+
+ @Nullable
+ @VisibleForTesting
+ CrossDeviceCall getCallForId(long crossDeviceCallId, Collection<CrossDeviceCall> calls) {
+ if (crossDeviceCallId == NOT_VALID) {
+ return null;
+ }
+ for (CrossDeviceCall crossDeviceCall : calls) {
+ if (crossDeviceCall.getId() == crossDeviceCallId) {
+ return crossDeviceCall;
+ }
+ }
+ return null;
}
@Override
public void onCallAdded(Call call) {
- onCallAdded(new CrossDeviceCall(call));
- }
-
- @VisibleForTesting
- void onCallAdded(CrossDeviceCall call) {
- mCurrentCalls.add(call);
+ if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
+ mCurrentCalls.put(call,
+ new CrossDeviceCall(getPackageManager(), call, getCallAudioState()));
+ }
}
@Override
public void onCallRemoved(Call call) {
- mCurrentCalls.removeIf(crossDeviceCall -> crossDeviceCall.getCall().equals(call));
- }
-
- /** Data holder for a telecom call and additional metadata. */
- public static final class CrossDeviceCall {
- private static final AtomicLong sNextId = new AtomicLong(1);
-
- private final Call mCall;
- private final long mId;
-
- public CrossDeviceCall(Call call) {
- mCall = call;
- mId = sNextId.getAndIncrement();
- }
-
- public Call getCall() {
- return mCall;
- }
-
- public long getId() {
- return mId;
+ if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
+ mCurrentCalls.remove(call);
}
}
-}
+
+ @Override
+ public void onMuteStateChanged(boolean isMuted) {
+ if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
+ mCurrentCalls.values().forEach(call -> call.updateMuted(isMuted));
+ }
+ }
+
+ @Override
+ public void onSilenceRinger() {
+ if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
+ mCurrentCalls.values().forEach(call -> call.updateSilencedIfRinging());
+ }
+ }
+
+ private void doMute() {
+ setMuted(/* shouldMute= */ true);
+ }
+
+ private void doUnmute() {
+ setMuted(/* shouldMute= */ false);
+ }
+
+ private void doSilence() {
+ final TelecomManager telecomManager = getSystemService(TelecomManager.class);
+ if (telecomManager != null) {
+ telecomManager.silenceRinger();
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/core/Android.bp b/services/core/Android.bp
index c0bb586..f65ed33 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -182,6 +182,7 @@
"SurfaceFlingerProperties",
"com.android.sysprop.watchdog",
"ImmutabilityAnnotation",
+ "securebox",
],
javac_shard_size: 50,
javacflags: [
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5f27841..10d50c2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -14256,6 +14256,16 @@
}
}
+ if (Process.isSdkSandboxUid(realCallingUid)) {
+ SdkSandboxManagerLocal sdkSandboxManagerLocal = LocalManagerRegistry.getManager(
+ SdkSandboxManagerLocal.class);
+ if (sdkSandboxManagerLocal == null) {
+ throw new IllegalStateException("SdkSandboxManagerLocal not found when sending"
+ + " a broadcast from an SDK sandbox uid.");
+ }
+ sdkSandboxManagerLocal.enforceAllowedToSendBroadcast(intent);
+ }
+
boolean timeoutExempt = false;
if (action != null) {
@@ -14266,16 +14276,6 @@
intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
}
- if (Process.isSdkSandboxUid(realCallingUid)) {
- SdkSandboxManagerLocal sdkSandboxManagerLocal = LocalManagerRegistry.getManager(
- SdkSandboxManagerLocal.class);
- if (sdkSandboxManagerLocal == null) {
- throw new IllegalStateException("SdkSandboxManagerLocal not found when sending"
- + " a broadcast from an SDK sandbox uid.");
- }
- sdkSandboxManagerLocal.enforceAllowedToSendBroadcast(intent);
- }
-
switch (action) {
case Intent.ACTION_MEDIA_SCANNER_SCAN_FILE:
UserManagerInternal umInternal = LocalServices.getService(
@@ -18498,6 +18498,12 @@
}
@Override
+ public boolean isProcessFrozen(int pid) {
+ enforceCallingPermission(permission.DUMP, "isProcessFrozen()");
+ return mOomAdjuster.mCachedAppOptimizer.isProcessFrozen(pid);
+ }
+
+ @Override
@ReasonCode
public int getBackgroundRestrictionExemptionReason(int uid) {
enforceCallingPermission(android.Manifest.permission.DEVICE_POWER,
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index d7cca60..a440f18 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -1113,6 +1113,7 @@
pw.print(" because ");
pw.print(reasonToString(mRunnableAtReason));
pw.println();
+ pw.print("mProcessCached="); pw.println(mProcessCached);
pw.increaseIndent();
if (mActive != null) {
dumpRecord("ACTIVE", now, pw, mActive, mActiveIndex);
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 410bc41..2689193 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -1509,6 +1509,12 @@
return profile;
}
+ boolean isProcessFrozen(int pid) {
+ synchronized (mProcLock) {
+ return mFrozenProcesses.contains(pid);
+ }
+ }
+
@VisibleForTesting
static final class SingleCompactionStats {
private static final float STATSD_SAMPLE_RATE = 0.1f;
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 8d3c21e..3357708 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -1121,11 +1121,15 @@
final String permissionCheck =
checkContentProviderPermission(cpi, callingPid, callingUid,
userId, checkUser, null);
- if (permissionCheck != null) {
+ final boolean grantCheck = mService.checkUriPermission(uri, callingPid, callingUid,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION , userId, null)
+ == PackageManager.PERMISSION_GRANTED;
+
+ if (!grantCheck && permissionCheck != null) {
FrameworkStatsLog.write(GET_TYPE_ACCESSED_WITHOUT_PERMISSION,
GET_TYPE_ACCESSED_WITHOUT_PERMISSION__LOCATION__AM_FRAMEWORK_PERMISSION,
callingUid, authority, type);
- } else if (cpi.forceUriPermissions
+ } else if (!grantCheck && cpi.forceUriPermissions
&& holder.provider.checkUriPermission(attributionSource,
uri, callingUid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
!= PermissionChecker.PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/backup/SystemBackupAgent.java b/services/core/java/com/android/server/backup/SystemBackupAgent.java
index 08dcccf..35df3ee 100644
--- a/services/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/services/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -86,7 +86,7 @@
private static final Set<String> sEligibleForMultiUser = Sets.newArraySet(
PERMISSION_HELPER, NOTIFICATION_HELPER, SYNC_SETTINGS_HELPER, APP_LOCALES_HELPER,
- ACCOUNT_MANAGER_HELPER, USAGE_STATS_HELPER);
+ ACCOUNT_MANAGER_HELPER, USAGE_STATS_HELPER, PREFERRED_HELPER);
private int mUserId = UserHandle.USER_SYSTEM;
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 10abfdd..05464c8 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -196,6 +196,11 @@
// available.
private float mScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ // The screen brightness level before clamping and throttling. This value needs to be stored
+ // for concurrent displays mode and passed to the additional displays which will do their own
+ // clamping and throttling.
+ private float mRawScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+
// The current display policy. This is useful, for example, for knowing when we're dozing,
// where the light sensor may not be available.
private int mDisplayPolicy = DisplayPowerRequest.POLICY_OFF;
@@ -380,6 +385,10 @@
return mScreenAutoBrightness;
}
+ float getRawAutomaticScreenBrightness() {
+ return mRawScreenAutoBrightness;
+ }
+
public boolean hasValidAmbientLux() {
return mAmbientLuxValid;
}
@@ -653,6 +662,7 @@
mPreThresholdLux = PowerManager.BRIGHTNESS_INVALID_FLOAT;
}
mScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mRawScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mPreThresholdBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mRecentLightSamples = 0;
mAmbientLightRingBuffer.clear();
@@ -915,6 +925,7 @@
float value = mCurrentBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName,
mForegroundAppCategory);
+ mRawScreenAutoBrightness = value;
float newScreenAutoBrightness = clampScreenBrightness(value);
// The min/max range can change for brightness due to HBM. See if the current brightness
diff --git a/services/core/java/com/android/server/display/DisplayControl.java b/services/core/java/com/android/server/display/DisplayControl.java
index 864510e..fa9a100 100644
--- a/services/core/java/com/android/server/display/DisplayControl.java
+++ b/services/core/java/com/android/server/display/DisplayControl.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.os.IBinder;
+import android.view.Display;
import java.util.Objects;
@@ -32,6 +33,9 @@
private static native void nativeOverrideHdrTypes(IBinder displayToken, int[] modes);
private static native long[] nativeGetPhysicalDisplayIds();
private static native IBinder nativeGetPhysicalDisplayToken(long physicalDisplayId);
+ private static native void nativeSetHdrConversionMode(int conversionMode,
+ int preferredHdrOutputType, int[] autoHdrTypes, int autoHdrTypesLength);
+ private static native int[] nativeGetSupportedHdrOutputTypes();
/**
* Create a display in SurfaceFlinger.
@@ -79,4 +83,20 @@
public static IBinder getPhysicalDisplayToken(long physicalDisplayId) {
return nativeGetPhysicalDisplayToken(physicalDisplayId);
}
+
+ /**
+ * @hide
+ */
+ public static void setHdrConversionMode(int conversionMode, int preferredHdrOutputType,
+ int[] autoHdrTypes) {
+ int length = autoHdrTypes != null ? autoHdrTypes.length : 0;
+ nativeSetHdrConversionMode(conversionMode, preferredHdrOutputType, autoHdrTypes, length);
+ }
+
+ /**
+ * @hide
+ */
+ public static @Display.HdrCapabilities.HdrType int[] getSupportedHdrOutputTypes() {
+ return nativeGetSupportedHdrOutputTypes();
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index e557b50..535129c 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -132,6 +132,16 @@
* <brightness>0.01</brightness>
* </brightnessThrottlingPoint>
* </brightnessThrottlingMap>
+ * <concurrentDisplaysBrightnessThrottlingMap>
+ * <brightnessThrottlingPoint>
+ * <thermalStatus>severe</thermalStatus>
+ * <brightness>0.07</brightness>
+ * </brightnessThrottlingPoint>
+ * <brightnessThrottlingPoint>
+ * <thermalStatus>critical</thermalStatus>
+ * <brightness>0.005</brightness>
+ * </brightnessThrottlingPoint>
+ * </concurrentDisplaysBrightnessThrottlingMap>
* </thermalThrottling>
*
* <refreshRate>
@@ -613,6 +623,8 @@
// overwritten value.
private BrightnessThrottlingData mBrightnessThrottlingData;
private BrightnessThrottlingData mOriginalBrightnessThrottlingData;
+ // The concurrent displays mode might need a stricter throttling policy
+ private BrightnessThrottlingData mConcurrentDisplaysBrightnessThrottlingData;
@VisibleForTesting
DisplayDeviceConfig(Context context) {
@@ -1258,13 +1270,21 @@
}
/**
- * @return brightness throttling data configuration data for the display.
+ * @return brightness throttling configuration data for the display.
*/
public BrightnessThrottlingData getBrightnessThrottlingData() {
return BrightnessThrottlingData.create(mBrightnessThrottlingData);
}
/**
+ * @return brightness throttling configuration data for the display for the concurrent
+ * displays mode.
+ */
+ public BrightnessThrottlingData getConcurrentDisplaysBrightnessThrottlingData() {
+ return BrightnessThrottlingData.create(mConcurrentDisplaysBrightnessThrottlingData);
+ }
+
+ /**
* @return Auto brightness darkening light debounce
*/
public long getAutoBrightnessDarkeningLightDebounce() {
@@ -1503,6 +1523,7 @@
loadBrightnessConstraintsFromConfigXml();
loadBrightnessMap(config);
loadBrightnessThrottlingMap(config);
+ loadConcurrentDisplaysBrightnessThrottlingMap(config);
loadHighBrightnessModeData(config);
loadQuirks(config);
loadBrightnessRamps(config);
@@ -1714,13 +1735,13 @@
private void loadBrightnessThrottlingMap(DisplayConfiguration config) {
final ThermalThrottling throttlingConfig = config.getThermalThrottling();
if (throttlingConfig == null) {
- Slog.i(TAG, "no thermal throttling config found");
+ Slog.i(TAG, "No thermal throttling config found");
return;
}
final BrightnessThrottlingMap map = throttlingConfig.getBrightnessThrottlingMap();
if (map == null) {
- Slog.i(TAG, "no brightness throttling map found");
+ Slog.i(TAG, "No brightness throttling map found");
return;
}
@@ -1747,6 +1768,43 @@
}
}
+ private void loadConcurrentDisplaysBrightnessThrottlingMap(DisplayConfiguration config) {
+ final ThermalThrottling throttlingConfig = config.getThermalThrottling();
+ if (throttlingConfig == null) {
+ Slog.i(TAG, "No concurrent displays thermal throttling config found");
+ return;
+ }
+
+ final BrightnessThrottlingMap map =
+ throttlingConfig.getConcurrentDisplaysBrightnessThrottlingMap();
+ if (map == null) {
+ Slog.i(TAG, "No concurrent displays brightness throttling map found");
+ return;
+ }
+
+ final List<BrightnessThrottlingPoint> points = map.getBrightnessThrottlingPoint();
+ // At least 1 point is guaranteed by the display device config schema
+ List<BrightnessThrottlingData.ThrottlingLevel> throttlingLevels =
+ new ArrayList<>(points.size());
+
+ boolean badConfig = false;
+ for (BrightnessThrottlingPoint point : points) {
+ ThermalStatus status = point.getThermalStatus();
+ if (!thermalStatusIsValid(status)) {
+ badConfig = true;
+ break;
+ }
+
+ throttlingLevels.add(new BrightnessThrottlingData.ThrottlingLevel(
+ convertThermalStatus(status), point.getBrightness().floatValue()));
+ }
+
+ if (!badConfig) {
+ mConcurrentDisplaysBrightnessThrottlingData =
+ BrightnessThrottlingData.create(throttlingLevels);
+ }
+ }
+
private void loadRefreshRateSetting(DisplayConfiguration config) {
final RefreshRateConfigs refreshRateConfigs =
(config == null) ? null : config.getRefreshRate();
@@ -2529,7 +2587,8 @@
}
}
- private @PowerManager.ThermalStatus int convertThermalStatus(ThermalStatus value) {
+ @VisibleForTesting
+ static @PowerManager.ThermalStatus int convertThermalStatus(ThermalStatus value) {
if (value == null) {
return PowerManager.THERMAL_STATUS_NONE;
}
@@ -2855,7 +2914,8 @@
return throttlingLevels.hashCode();
}
- private BrightnessThrottlingData(List<ThrottlingLevel> inLevels) {
+ @VisibleForTesting
+ BrightnessThrottlingData(List<ThrottlingLevel> inLevels) {
throttlingLevels = new ArrayList<>(inLevels.size());
for (ThrottlingLevel level : inLevels) {
throttlingLevels.add(new ThrottlingLevel(level.thermalStatus, level.brightness));
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c1d8cf4..f2110fe 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -79,6 +79,7 @@
import android.hardware.display.DisplayViewport;
import android.hardware.display.DisplayedContentSample;
import android.hardware.display.DisplayedContentSamplingAttributes;
+import android.hardware.display.HdrConversionMode;
import android.hardware.display.IDisplayManager;
import android.hardware.display.IDisplayManagerCallback;
import android.hardware.display.IVirtualDisplayCallback;
@@ -233,11 +234,17 @@
private InputManagerInternal mInputManagerInternal;
private IMediaProjectionManager mProjectionService;
private DeviceStateManagerInternal mDeviceStateManager;
+ @GuardedBy("mSyncRoot")
private int[] mUserDisabledHdrTypes = {};
+ private int[] mSupportedHdrOutputType;
+ @GuardedBy("mSyncRoot")
private boolean mAreUserDisabledHdrTypesAllowed = true;
// Display mode chosen by user.
private Display.Mode mUserPreferredMode;
+ // HDR conversion mode chosen by user
+ @GuardedBy("mSyncRoot")
+ private HdrConversionMode mHdrConversionMode;
// The synchronization root for the display manager.
// This lock guards most of the display manager's state.
@@ -880,8 +887,8 @@
}
private void clearUserDisabledHdrTypesLocked() {
- mUserDisabledHdrTypes = new int[]{};
synchronized (mSyncRoot) {
+ mUserDisabledHdrTypes = new int[]{};
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.USER_DISABLED_HDR_FORMATS, "");
}
@@ -1907,6 +1914,24 @@
});
}
+ @GuardedBy("mSyncRoot")
+ private int[] getEnabledAutoHdrTypesLocked() {
+ IntArray autoHdrOutputTypesArray = new IntArray();
+ for (int type : getSupportedHdrOutputTypesInternal()) {
+ boolean isDisabled = false;
+ for (int disabledType : mUserDisabledHdrTypes) {
+ if (type == disabledType) {
+ isDisabled = true;
+ break;
+ }
+ }
+ if (!isDisabled) {
+ autoHdrOutputTypesArray.add(type);
+ }
+ }
+ return autoHdrOutputTypesArray.toArray();
+ }
+
Display.Mode getUserPreferredDisplayModeInternal(int displayId) {
synchronized (mSyncRoot) {
if (displayId == Display.INVALID_DISPLAY) {
@@ -1930,6 +1955,38 @@
}
}
+ // TODO (b/264979880) - Add unit test for HDR output control methods.
+ private void setHdrConversionModeInternal(HdrConversionMode hdrConversionMode) {
+ int[] autoHdrOutputTypes = null;
+ synchronized (mSyncRoot) {
+ mHdrConversionMode = hdrConversionMode;
+
+ // For auto mode, all supported HDR types are allowed except the ones specifically
+ // disabled by the user.
+ if (hdrConversionMode.getConversionMode() == HdrConversionMode.HDR_CONVERSION_SYSTEM) {
+ autoHdrOutputTypes = getEnabledAutoHdrTypesLocked();
+ }
+ DisplayControl.setHdrConversionMode(hdrConversionMode.getConversionMode(),
+ hdrConversionMode.getPreferredHdrOutputType(), autoHdrOutputTypes);
+ }
+ }
+
+ private HdrConversionMode getHdrConversionModeInternal() {
+ synchronized (mSyncRoot) {
+ if (mHdrConversionMode != null) {
+ return mHdrConversionMode;
+ }
+ }
+ return new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_SYSTEM);
+ }
+
+ private @Display.HdrCapabilities.HdrType int[] getSupportedHdrOutputTypesInternal() {
+ if (mSupportedHdrOutputType == null) {
+ mSupportedHdrOutputType = DisplayControl.getSupportedHdrOutputTypes();
+ }
+ return mSupportedHdrOutputType;
+ }
+
void setShouldAlwaysRespectAppRequestedModeInternal(boolean enabled) {
mDisplayModeDirector.setShouldAlwaysRespectAppRequestedMode(enabled);
}
@@ -3191,7 +3248,9 @@
@Override // Binder call
public int[] getUserDisabledHdrTypes() {
- return mUserDisabledHdrTypes;
+ synchronized (mSyncRoot) {
+ return mUserDisabledHdrTypes;
+ }
}
@Override // Binder call
@@ -3610,6 +3669,40 @@
}
@Override // Binder call
+ public void setHdrConversionMode(HdrConversionMode hdrConversionMode) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.MODIFY_HDR_CONVERSION_MODE,
+ "Permission required to set the HDR conversion mode.");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ setHdrConversionModeInternal(hdrConversionMode);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public HdrConversionMode getHdrConversionMode() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return getHdrConversionModeInternal();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Display.HdrCapabilities.HdrType
+ @Override // Binder call
+ public int[] getSupportedHdrOutputTypes() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return getSupportedHdrOutputTypesInternal();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS,
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 263c4d7..dbd2ab0 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -885,6 +885,9 @@
updatePowerState();
}
});
+
+ // TODO (b/265793751): Re-create BrightnessTracker if we're enetering/exiting concurrent
+ // displays mode
}
/**
@@ -943,6 +946,8 @@
return mDisplayDeviceConfig.getHdrBrightnessFromSdr(sdrBrightness);
}
});
+ // TODO (b/265793751): Use the appropriate throttling data if we're in concurrent displays
+ // mode
mBrightnessThrottler.resetThrottlingData(
mDisplayDeviceConfig.getBrightnessThrottlingData(), mUniqueDisplayId);
}
@@ -1160,6 +1165,8 @@
}
loadScreenOffBrightnessSensor();
int[] sensorValueToLux = mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux();
+ // TODO (b/265793751): Don't instantiate ScreenOffBrightnessSensorController if this is
+ // a complementary display
if (mScreenOffBrightnessSensor != null && sensorValueToLux != null) {
mScreenOffBrightnessSensorController = new ScreenOffBrightnessSensorController(
mSensorManager,
@@ -1452,7 +1459,8 @@
final boolean autoBrightnessEnabled = mUseAutoBrightness
&& (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
&& Float.isNaN(brightnessState)
- && mAutomaticBrightnessController != null;
+ && mAutomaticBrightnessController != null
+ && mBrightnessReasonTemp.getReason() != BrightnessReason.REASON_FOLLOWER;
final boolean autoBrightnessDisabledDueToDisplayOff = mUseAutoBrightness
&& !(state == Display.STATE_ON || autoBrightnessEnabledInDoze);
final int autoBrightnessState = autoBrightnessEnabled
@@ -1521,12 +1529,15 @@
}
boolean updateScreenBrightnessSetting = false;
+ float rawBrightnessState = brightnessState;
// Apply auto-brightness.
boolean slowChange = false;
if (Float.isNaN(brightnessState)) {
float newAutoBrightnessAdjustment = autoBrightnessAdjustment;
if (autoBrightnessEnabled) {
+ rawBrightnessState = mAutomaticBrightnessController
+ .getRawAutomaticScreenBrightness();
brightnessState = mAutomaticBrightnessController.getAutomaticScreenBrightness(
mTempBrightnessEvent);
newAutoBrightnessAdjustment =
@@ -1568,7 +1579,8 @@
// Use default brightness when dozing unless overridden.
if ((Float.isNaN(brightnessState))
&& Display.isDozeState(state)) {
- brightnessState = clampScreenBrightness(mScreenBrightnessDozeConfig);
+ rawBrightnessState = mScreenBrightnessDozeConfig;
+ brightnessState = clampScreenBrightness(rawBrightnessState);
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
}
@@ -1576,7 +1588,9 @@
// brightness
if (Float.isNaN(brightnessState) && autoBrightnessEnabled
&& mScreenOffBrightnessSensorController != null) {
- brightnessState = mScreenOffBrightnessSensorController.getAutomaticScreenBrightness();
+ rawBrightnessState =
+ mScreenOffBrightnessSensorController.getAutomaticScreenBrightness();
+ brightnessState = rawBrightnessState;
if (isValidBrightnessValue(brightnessState)) {
brightnessState = clampScreenBrightness(brightnessState);
updateScreenBrightnessSetting = mCurrentScreenBrightnessSetting != brightnessState;
@@ -1587,7 +1601,8 @@
// Apply manual brightness.
if (Float.isNaN(brightnessState)) {
- brightnessState = clampScreenBrightness(mCurrentScreenBrightnessSetting);
+ rawBrightnessState = mCurrentScreenBrightnessSetting;
+ brightnessState = clampScreenBrightness(rawBrightnessState);
if (brightnessState != mCurrentScreenBrightnessSetting) {
// The manually chosen screen brightness is outside of the currently allowed
// range (i.e., high-brightness-mode), make sure we tell the rest of the system
@@ -1621,7 +1636,7 @@
for (int i = 0; i < displayBrightnessFollowers.size(); i++) {
DisplayPowerControllerInterface follower = displayBrightnessFollowers.valueAt(i);
- follower.setBrightnessToFollow(brightnessState, convertToNits(brightnessState));
+ follower.setBrightnessToFollow(rawBrightnessState, convertToNits(rawBrightnessState));
}
if (updateScreenBrightnessSetting) {
@@ -2038,6 +2053,8 @@
private BrightnessThrottler createBrightnessThrottlerLocked() {
final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
+ // TODO (b/265793751): Use the appropriate throttling data if we're in concurrent displays
+ // mode
final DisplayDeviceConfig.BrightnessThrottlingData data =
ddConfig != null ? ddConfig.getBrightnessThrottlingData() : null;
return new BrightnessThrottler(mHandler, data,
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 0cc4e93..103a1da 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -732,6 +732,9 @@
updatePowerState();
}
});
+
+ // TODO (b/265793751): Re-create BrightnessTracker if we're enetering/exiting concurrent
+ // displays mode
}
/**
@@ -783,6 +786,8 @@
return mDisplayDeviceConfig.getHdrBrightnessFromSdr(sdrBrightness);
}
});
+ // TODO (b/265793751): Use the appropriate throttling data if we're in concurrent displays
+ // mode
mBrightnessThrottler.resetThrottlingData(
mDisplayDeviceConfig.getBrightnessThrottlingData(), mUniqueDisplayId);
}
@@ -999,6 +1004,8 @@
loadScreenOffBrightnessSensor();
int[] sensorValueToLux = mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux();
+ // TODO (b/265793751): Don't instantiate ScreenOffBrightnessSensorController if this is
+ // a complementary display
if (mScreenOffBrightnessSensor != null && sensorValueToLux != null) {
mScreenOffBrightnessSensorController = new ScreenOffBrightnessSensorController(
mSensorManager,
@@ -1175,6 +1182,7 @@
DisplayBrightnessState displayBrightnessState = mDisplayBrightnessController
.updateBrightness(mPowerRequest, state);
float brightnessState = displayBrightnessState.getBrightness();
+ float rawBrightnessState = displayBrightnessState.getBrightness();
mBrightnessReasonTemp.set(displayBrightnessState.getBrightnessReason());
final boolean autoBrightnessEnabledInDoze =
@@ -1185,7 +1193,8 @@
&& (Float.isNaN(brightnessState)
|| mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_TEMPORARY
|| mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_BOOST)
- && mAutomaticBrightnessController != null;
+ && mAutomaticBrightnessController != null
+ && mBrightnessReasonTemp.getReason() != BrightnessReason.REASON_FOLLOWER;
final boolean autoBrightnessDisabledDueToDisplayOff = mUseAutoBrightness
&& !(state == Display.STATE_ON || autoBrightnessEnabledInDoze);
final int autoBrightnessState = autoBrightnessEnabled
@@ -1239,6 +1248,8 @@
if (Float.isNaN(brightnessState)) {
float newAutoBrightnessAdjustment = autoBrightnessAdjustment;
if (autoBrightnessEnabled) {
+ rawBrightnessState = mAutomaticBrightnessController
+ .getRawAutomaticScreenBrightness();
brightnessState = mAutomaticBrightnessController.getAutomaticScreenBrightness(
mTempBrightnessEvent);
newAutoBrightnessAdjustment =
@@ -1280,7 +1291,8 @@
// Use default brightness when dozing unless overridden.
if ((Float.isNaN(brightnessState))
&& Display.isDozeState(state)) {
- brightnessState = clampScreenBrightness(mScreenBrightnessDozeConfig);
+ rawBrightnessState = mScreenBrightnessDozeConfig;
+ brightnessState = clampScreenBrightness(rawBrightnessState);
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
}
@@ -1288,7 +1300,9 @@
// brightness
if (Float.isNaN(brightnessState) && autoBrightnessEnabled
&& mScreenOffBrightnessSensorController != null) {
- brightnessState = mScreenOffBrightnessSensorController.getAutomaticScreenBrightness();
+ rawBrightnessState =
+ mScreenOffBrightnessSensorController.getAutomaticScreenBrightness();
+ brightnessState = rawBrightnessState;
if (BrightnessUtils.isValidBrightnessValue(brightnessState)) {
brightnessState = clampScreenBrightness(brightnessState);
updateScreenBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness()
@@ -1300,7 +1314,8 @@
// Apply manual brightness.
if (Float.isNaN(brightnessState)) {
- brightnessState = clampScreenBrightness(currentBrightnessSetting);
+ rawBrightnessState = currentBrightnessSetting;
+ brightnessState = clampScreenBrightness(rawBrightnessState);
if (brightnessState != currentBrightnessSetting) {
// The manually chosen screen brightness is outside of the currently allowed
// range (i.e., high-brightness-mode), make sure we tell the rest of the system
@@ -1334,7 +1349,7 @@
for (int i = 0; i < displayBrightnessFollowers.size(); i++) {
DisplayPowerControllerInterface follower = displayBrightnessFollowers.valueAt(i);
- follower.setBrightnessToFollow(brightnessState, convertToNits(brightnessState));
+ follower.setBrightnessToFollow(rawBrightnessState, convertToNits(rawBrightnessState));
}
if (updateScreenBrightnessSetting) {
@@ -1745,6 +1760,8 @@
private BrightnessThrottler createBrightnessThrottlerLocked() {
final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
+ // TODO (b/265793751): Use the appropriate throttling data if we're in concurrent displays
+ // mode
final DisplayDeviceConfig.BrightnessThrottlingData data =
ddConfig != null ? ddConfig.getBrightnessThrottlingData() : null;
return new BrightnessThrottler(mHandler, data,
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 282ad57..262013e 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -113,9 +113,8 @@
import com.android.internal.util.HexDump;
import com.android.server.FgThread;
import com.android.server.location.gnss.GnssSatelliteBlocklistHelper.GnssSatelliteBlocklistCallback;
-import com.android.server.location.gnss.NtpTimeHelper.InjectNtpTimeCallback;
+import com.android.server.location.gnss.NetworkTimeHelper.InjectTimeCallback;
import com.android.server.location.gnss.hal.GnssNative;
-import com.android.server.location.injector.Injector;
import com.android.server.location.provider.AbstractLocationProvider;
import java.io.FileDescriptor;
@@ -138,7 +137,7 @@
* {@hide}
*/
public class GnssLocationProvider extends AbstractLocationProvider implements
- InjectNtpTimeCallback, GnssSatelliteBlocklistCallback, GnssNative.BaseCallbacks,
+ InjectTimeCallback, GnssSatelliteBlocklistCallback, GnssNative.BaseCallbacks,
GnssNative.LocationCallbacks, GnssNative.SvStatusCallbacks, GnssNative.AGpsCallbacks,
GnssNative.PsdsCallbacks, GnssNative.NotificationCallbacks,
GnssNative.LocationRequestCallbacks, GnssNative.TimeCallbacks {
@@ -307,7 +306,7 @@
private boolean mSuplEsEnabled = false;
private final LocationExtras mLocationExtras = new LocationExtras();
- private final NtpTimeHelper mNtpTimeHelper;
+ private final NetworkTimeHelper mNetworkTimeHelper;
private final GnssSatelliteBlocklistHelper mGnssSatelliteBlocklistHelper;
// Available only on GNSS HAL 2.0 implementations and later.
@@ -398,7 +397,7 @@
}
}
- public GnssLocationProvider(Context context, Injector injector, GnssNative gnssNative,
+ public GnssLocationProvider(Context context, GnssNative gnssNative,
GnssMetrics gnssMetrics) {
super(FgThread.getExecutor(), CallerIdentity.fromContext(context), PROPERTIES,
Collections.emptySet());
@@ -470,7 +469,7 @@
GnssLocationProvider.this::onNetworkAvailable,
mHandler.getLooper(), mNIHandler);
- mNtpTimeHelper = new NtpTimeHelper(mContext, mHandler.getLooper(), this);
+ mNetworkTimeHelper = NetworkTimeHelper.create(mContext, mHandler.getLooper(), this);
mGnssSatelliteBlocklistHelper =
new GnssSatelliteBlocklistHelper(mContext,
mHandler.getLooper(), this);
@@ -647,18 +646,19 @@
}
/**
- * Implements {@link InjectNtpTimeCallback#injectTime}
+ * Implements {@link InjectTimeCallback#injectTime}
*/
@Override
- public void injectTime(long time, long timeReference, int uncertainty) {
- mGnssNative.injectTime(time, timeReference, uncertainty);
+ public void injectTime(long unixEpochTimeMillis, long elapsedRealtimeMillis,
+ int uncertaintyMillis) {
+ mGnssNative.injectTime(unixEpochTimeMillis, elapsedRealtimeMillis, uncertaintyMillis);
}
/**
* Implements {@link GnssNetworkConnectivityHandler.GnssNetworkListener#onNetworkAvailable()}
*/
private void onNetworkAvailable() {
- mNtpTimeHelper.onNetworkAvailable();
+ mNetworkTimeHelper.onNetworkAvailable();
// Download only if supported, (prevents an unnecessary on-boot download)
if (mSupportsPsds) {
synchronized (mLock) {
@@ -1145,7 +1145,7 @@
if ("delete_aiding_data".equals(command)) {
deleteAidingData(extras);
} else if ("force_time_injection".equals(command)) {
- requestUtcTime();
+ demandUtcTimeInjection();
} else if ("force_psds_injection".equals(command)) {
if (mSupportsPsds) {
postWithWakeLockHeld(() -> handleDownloadPsdsData(
@@ -1514,9 +1514,9 @@
/* userResponse= */ 0);
}
- private void requestUtcTime() {
- if (DEBUG) Log.d(TAG, "utcTimeRequest");
- postWithWakeLockHeld(mNtpTimeHelper::retrieveAndInjectNtpTime);
+ private void demandUtcTimeInjection() {
+ if (DEBUG) Log.d(TAG, "demandUtcTimeInjection");
+ postWithWakeLockHeld(mNetworkTimeHelper::demandUtcTimeInjection);
}
@@ -1721,9 +1721,16 @@
public void onCapabilitiesChanged(GnssCapabilities oldCapabilities,
GnssCapabilities newCapabilities) {
mHandler.post(() -> {
- if (mGnssNative.getCapabilities().hasOnDemandTime()) {
- mNtpTimeHelper.enablePeriodicTimeInjection();
- requestUtcTime();
+ boolean useOnDemandTimeInjection = mGnssNative.getCapabilities().hasOnDemandTime();
+
+ // b/73893222: There is a historic bug on Android, which means that the capability
+ // "on demand time" is interpreted as "enable periodic injection" elsewhere but an
+ // on-demand injection is done here. GNSS developers may have come to rely on the
+ // periodic behavior, so it has been kept and all methods named to reflect what is
+ // actually done. "On demand" requests are supported regardless of the capability.
+ mNetworkTimeHelper.setPeriodicTimeInjectionMode(useOnDemandTimeInjection);
+ if (useOnDemandTimeInjection) {
+ demandUtcTimeInjection();
}
restartLocationRequest();
@@ -1857,7 +1864,7 @@
@Override
public void onRequestUtcTime() {
- requestUtcTime();
+ demandUtcTimeInjection();
}
@Override
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 69385a9..2174f40 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -83,8 +83,7 @@
mGnssMetrics = new GnssMetrics(mContext, IBatteryStats.Stub.asInterface(
ServiceManager.getService(BatteryStats.SERVICE_NAME)), mGnssNative);
- mGnssLocationProvider = new GnssLocationProvider(mContext, injector, mGnssNative,
- mGnssMetrics);
+ mGnssLocationProvider = new GnssLocationProvider(mContext, mGnssNative, mGnssMetrics);
mGnssStatusProvider = new GnssStatusProvider(injector, mGnssNative);
mGnssNmeaProvider = new GnssNmeaProvider(injector, mGnssNative);
mGnssMeasurementsProvider = new GnssMeasurementsProvider(injector, mGnssNative);
diff --git a/services/core/java/com/android/server/location/gnss/NetworkTimeHelper.java b/services/core/java/com/android/server/location/gnss/NetworkTimeHelper.java
new file mode 100644
index 0000000..72d6f70
--- /dev/null
+++ b/services/core/java/com/android/server/location/gnss/NetworkTimeHelper.java
@@ -0,0 +1,75 @@
+/*
+ * 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.location.gnss;
+
+import android.content.Context;
+import android.os.Looper;
+
+/**
+ * An abstraction for use by {@link GnssLocationProvider}. This class allows switching between
+ * implementations with a compile-time constant change, which is less risky than rolling back a
+ * whole class. When there is a single implementation again this class can be replaced by that
+ * implementation.
+ */
+abstract class NetworkTimeHelper {
+
+ /**
+ * The callback interface used by {@link NetworkTimeHelper} to report the time to {@link
+ * GnssLocationProvider}. The callback can happen at any time using the thread associated with
+ * the looper passed to {@link #create(Context, Looper, InjectTimeCallback)}.
+ */
+ interface InjectTimeCallback {
+ void injectTime(long unixEpochTimeMillis, long elapsedRealtimeMillis,
+ int uncertaintyMillis);
+ }
+
+ /**
+ * Creates the {@link NetworkTimeHelper} instance for use by {@link GnssLocationProvider}.
+ */
+ static NetworkTimeHelper create(
+ Context context, Looper looper, InjectTimeCallback injectTimeCallback) {
+ return new NtpNetworkTimeHelper(context, looper, injectTimeCallback);
+ }
+
+ /**
+ * Sets the "on demand time injection" mode.
+ *
+ * <p>Called by {@link GnssLocationProvider} to set the expected time injection behavior.
+ * When {@code enablePeriodicTimeInjection == true}, the time helper should periodically send
+ * the time on an undefined schedule. The time can be injected at other times for other reasons
+ * as well as be requested via {@link #demandUtcTimeInjection()}.
+ *
+ * @param periodicTimeInjectionEnabled {@code true} if the GNSS implementation requires periodic
+ * time signals
+ */
+ abstract void setPeriodicTimeInjectionMode(boolean periodicTimeInjectionEnabled);
+
+ /**
+ * Requests an asynchronous time injection via {@link InjectTimeCallback#injectTime}, if a
+ * network time is available. {@link InjectTimeCallback#injectTime} may not be called if a
+ * network time is not available.
+ */
+ abstract void demandUtcTimeInjection();
+
+ /**
+ * Notifies that network connectivity has been established.
+ *
+ * <p>Called by {@link GnssLocationProvider} when the device establishes a data network
+ * connection.
+ */
+ abstract void onNetworkAvailable();
+
+}
diff --git a/services/core/java/com/android/server/location/gnss/NtpTimeHelper.java b/services/core/java/com/android/server/location/gnss/NtpNetworkTimeHelper.java
similarity index 83%
rename from services/core/java/com/android/server/location/gnss/NtpTimeHelper.java
rename to services/core/java/com/android/server/location/gnss/NtpNetworkTimeHelper.java
index e4bc788..479dbda 100644
--- a/services/core/java/com/android/server/location/gnss/NtpTimeHelper.java
+++ b/services/core/java/com/android/server/location/gnss/NtpNetworkTimeHelper.java
@@ -30,14 +30,11 @@
import com.android.internal.annotations.VisibleForTesting;
/**
- * Handles inject NTP time to GNSS.
- *
- * <p>The client is responsible to call {@link #onNetworkAvailable()} when network is available
- * for retrieving NTP Time.
+ * Handles injecting network time to GNSS by explicitly making NTP requests when needed.
*/
-class NtpTimeHelper {
+class NtpNetworkTimeHelper extends NetworkTimeHelper {
- private static final String TAG = "NtpTimeHelper";
+ private static final String TAG = "NtpNetworkTimeHelper";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
// states for injecting ntp
@@ -71,23 +68,19 @@
private final WakeLock mWakeLock;
private final Handler mHandler;
- private final InjectNtpTimeCallback mCallback;
+ private final InjectTimeCallback mCallback;
// flags to trigger NTP when network becomes available
// initialized to STATE_PENDING_NETWORK so we do NTP when the network comes up after booting
@GuardedBy("this")
private int mInjectNtpTimeState = STATE_PENDING_NETWORK;
- // set to true if the GPS engine requested on-demand NTP time requests
+ // Enables periodic time injection in addition to injection for other reasons.
@GuardedBy("this")
- private boolean mOnDemandTimeInjection;
-
- interface InjectNtpTimeCallback {
- void injectTime(long time, long timeReference, int uncertainty);
- }
+ private boolean mPeriodicTimeInjection;
@VisibleForTesting
- NtpTimeHelper(Context context, Looper looper, InjectNtpTimeCallback callback,
+ NtpNetworkTimeHelper(Context context, Looper looper, InjectTimeCallback callback,
NtpTrustedTime ntpTime) {
mConnMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
mCallback = callback;
@@ -97,14 +90,23 @@
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
}
- NtpTimeHelper(Context context, Looper looper, InjectNtpTimeCallback callback) {
+ NtpNetworkTimeHelper(Context context, Looper looper, InjectTimeCallback callback) {
this(context, looper, callback, NtpTrustedTime.getInstance(context));
}
- synchronized void enablePeriodicTimeInjection() {
- mOnDemandTimeInjection = true;
+ @Override
+ synchronized void setPeriodicTimeInjectionMode(boolean periodicTimeInjectionEnabled) {
+ if (periodicTimeInjectionEnabled) {
+ mPeriodicTimeInjection = true;
+ }
}
+ @Override
+ void demandUtcTimeInjection() {
+ retrieveAndInjectNtpTime();
+ }
+
+ @Override
synchronized void onNetworkAvailable() {
if (mInjectNtpTimeState == STATE_PENDING_NETWORK) {
retrieveAndInjectNtpTime();
@@ -120,7 +122,7 @@
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}
- synchronized void retrieveAndInjectNtpTime() {
+ private synchronized void retrieveAndInjectNtpTime() {
if (mInjectNtpTimeState == STATE_RETRIEVING_AND_INJECTING) {
// already downloading data
return;
@@ -166,18 +168,15 @@
if (DEBUG) {
Log.d(TAG, String.format(
- "onDemandTimeInjection=%s, refreshSuccess=%s, delay=%s",
- mOnDemandTimeInjection,
+ "mPeriodicTimeInjection=%s, refreshSuccess=%s, delay=%s",
+ mPeriodicTimeInjection,
refreshSuccess,
delay));
}
- // TODO(b/73893222): reconcile Capabilities bit 'on demand' name vs. de facto periodic
- // injection.
- if (mOnDemandTimeInjection || !refreshSuccess) {
- /* Schedule next NTP injection.
- * Since this is delayed, the wake lock is released right away, and will be held
- * again when the delayed task runs.
- */
+ if (mPeriodicTimeInjection || !refreshSuccess) {
+ // Schedule next NTP injection.
+ // Since this is delayed, the wake lock is released right away, and will be held
+ // again when the delayed task runs.
mHandler.postDelayed(this::retrieveAndInjectNtpTime, delay);
}
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
index 7921619..2818caa 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
@@ -21,6 +21,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import com.android.security.SecureBox;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 7c80d8a..b437421 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -44,6 +44,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
+import com.android.security.SecureBox;
import com.android.server.locksettings.recoverablekeystore.certificate.CertParsingException;
import com.android.server.locksettings.recoverablekeystore.certificate.CertUtils;
import com.android.server.locksettings.recoverablekeystore.certificate.CertValidationException;
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorage.java
index b94548b..267a72e 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorage.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorage.java
@@ -23,7 +23,7 @@
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.locksettings.recoverablekeystore.SecureBox;
+import com.android.security.SecureBox;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index a7e0af3..e0de294 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -43,6 +43,7 @@
import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.UserInfo;
+import android.content.pm.UserProperties;
import android.content.pm.VersionedPackage;
import android.net.Uri;
import android.os.Binder;
@@ -776,19 +777,23 @@
userId, deleteFlags, false /*removedBySystem*/);
// Get a list of child user profiles and delete if package is
- // present in clone profile.
+ // present in that profile.
int[] childUserIds = mUserManagerInternal.getProfileIds(userId, true);
+ int returnCodeOfChild;
for (int childId : childUserIds) {
- if (childId != userId) {
- UserInfo userInfo = mUserManagerInternal.getUserInfo(childId);
- if (userInfo != null && userInfo.isCloneProfile()) {
- returnCode = deletePackageX(internalPackageName, versionCode,
- childId, deleteFlags, false /*removedBySystem*/);
- break;
+ if (childId == userId) continue;
+ UserProperties userProperties = mUserManagerInternal
+ .getUserProperties(childId);
+ if (userProperties != null && userProperties.getDeleteAppWithParent()) {
+ returnCodeOfChild = deletePackageX(internalPackageName, versionCode,
+ childId, deleteFlags, false /*removedBySystem*/);
+ if (returnCodeOfChild != PackageManager.DELETE_SUCCEEDED) {
+ Slog.w(TAG, "Package delete failed for user " + childId
+ + ", returnCode " + returnCodeOfChild);
+ returnCode = PackageManager.DELETE_FAILED_FOR_CHILD_PROFILE;
}
}
}
-
} else {
int[] blockUninstallUserIds = getBlockUninstallForUsers(innerSnapshot,
internalPackageName, users);
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index b8c57b8..77c32ae 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -136,7 +136,7 @@
.CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_NO_FILTERING)
.setMediaSharedWithParent(true)
.setCredentialShareableWithParent(true)
- );
+ .setDeleteAppWithParent(true));
}
/**
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 58f88c3..e74b459 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -206,6 +206,8 @@
static {
SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS);
SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_BACKGROUND);
+ SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE);
+ SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND);
}
private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>();
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 8973adc..052c785 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1313,7 +1313,9 @@
// Bg location is one-off runtime modifier permission and has no app op
if (sPlatformPermissions.containsKey(permission)
&& !Manifest.permission.ACCESS_BACKGROUND_LOCATION.equals(permission)
- && !Manifest.permission.BODY_SENSORS_BACKGROUND.equals(permission)) {
+ && !Manifest.permission.BODY_SENSORS_BACKGROUND.equals(permission)
+ && !Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND
+ .equals(permission)) {
Slog.wtf(LOG_TAG, "Platform runtime permission " + permission
+ " with no app op defined!");
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4e9cd2e..e0bcc0e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3770,9 +3770,6 @@
private boolean setKeyguardOccludedLw(boolean isOccluded, boolean notify) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "setKeyguardOccluded occluded=" + isOccluded);
mKeyguardOccludedChanged = false;
- if (isKeyguardOccluded() == isOccluded) {
- return false;
- }
mKeyguardDelegate.setOccluded(isOccluded, notify);
return mKeyguardDelegate.isShowing();
}
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index a82d4ea..5096ad1 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -61,6 +61,7 @@
public final class ShutdownThread extends Thread {
// constants
+ private static final boolean DEBUG = false;
private static final String TAG = "ShutdownThread";
private static final int ACTION_DONE_POLL_WAIT_MS = 500;
private static final int RADIOS_STATE_POLL_SLEEP_MS = 100;
@@ -161,7 +162,9 @@
// any additional calls are just returned
synchronized (sIsStartedGuard) {
if (sIsStarted) {
- Log.d(TAG, "Request to shutdown already running, returning.");
+ if (DEBUG) {
+ Log.d(TAG, "Request to shutdown already running, returning.");
+ }
return;
}
}
@@ -178,7 +181,9 @@
? com.android.internal.R.string.shutdown_confirm_question
: com.android.internal.R.string.shutdown_confirm);
- Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
+ if (DEBUG) {
+ Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
+ }
if (confirm) {
final CloseDialogReceiver closer = new CloseDialogReceiver(context);
@@ -348,26 +353,34 @@
}
private static boolean showSysuiReboot() {
- Log.d(TAG, "Attempting to use SysUI shutdown UI");
+ if (DEBUG) {
+ Log.d(TAG, "Attempting to use SysUI shutdown UI");
+ }
try {
StatusBarManagerInternal service = LocalServices.getService(
StatusBarManagerInternal.class);
if (service.showShutdownUi(mReboot, mReason)) {
// Sysui will handle shutdown UI.
- Log.d(TAG, "SysUI handling shutdown UI");
+ if (DEBUG) {
+ Log.d(TAG, "SysUI handling shutdown UI");
+ }
return true;
}
} catch (Exception e) {
// If anything went wrong, ignore it and use fallback ui
}
- Log.d(TAG, "SysUI is unavailable");
+ if (DEBUG) {
+ Log.d(TAG, "SysUI is unavailable");
+ }
return false;
}
private static void beginShutdownSequence(Context context) {
synchronized (sIsStartedGuard) {
if (sIsStarted) {
- Log.d(TAG, "Shutdown sequence already running, returning.");
+ if (DEBUG) {
+ Log.d(TAG, "Shutdown sequence already running, returning.");
+ }
return;
}
sIsStarted = true;
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index e9badef..718a9e3 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -20,47 +20,63 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.KEYGUARD_STATE_AOD_SHOWN;
+import static android.view.WindowManager.KEYGUARD_STATE_DREAMING;
+import static android.view.WindowManager.KEYGUARD_STATE_GOING_AWAY;
+import static android.view.WindowManager.KEYGUARD_STATE_KEYGUARD_TOP;
+import static android.view.WindowManager.KEYGUARD_STATE_LOCKSCREEN_SHOWN;
+import static android.view.WindowManager.KEYGUARD_STATE_OCCLUDED;
+import static android.view.WindowManager.KEYGUARD_STATE_OFF;
+import static android.view.WindowManager.KEYGUARD_STATE_ON;
+import static android.view.WindowManager.KEYGUARD_STATE_ROOT;
+import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
-import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
-import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS;
-import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
-import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
-import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_WAKE;
+import static android.view.WindowManager.keyguardStateToString;
+import static android.view.WindowManager.transitTypeToString;
+import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD;
+
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.KeyguardControllerProto.AOD_SHOWING;
-import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_GOING_AWAY;
import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_PER_DISPLAY;
import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_SHOWING;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.os.Debug;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.Trace;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
-import android.view.Display;
import android.view.WindowManager;
+import android.view.WindowManager.KeyguardState;
+import android.window.TransitionInfo;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.utils.StateMachine;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
/**
* Controls Keyguard occluding, dismissing and transitions depending on what kind of activities are
@@ -69,281 +85,138 @@
* Note that everything in this class should only be accessed with the AM lock being held.
*/
class KeyguardController {
-
private static final String TAG = TAG_WITH_CLASS_NAME ? "KeyguardController" : TAG_ATM;
+ private static final boolean DEBUG = true;
+
static final String KEYGUARD_SLEEP_TOKEN_TAG = "keyguard";
private static final int DEFER_WAKE_TRANSITION_TIMEOUT_MS = 5000;
- private final ActivityTaskSupervisor mTaskSupervisor;
- private WindowManagerService mWindowManager;
-
- private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>();
private final ActivityTaskManagerService mService;
- private RootWindowContainer mRootWindowContainer;
+ private final ActivityTaskSupervisor mTaskSupervisor;
private final ActivityTaskManagerInternal.SleepTokenAcquirer mSleepTokenAcquirer;
private boolean mWaitingForWakeTransition;
+ private WindowManagerService mWindowManager;
+
+ private RootWindowContainer mRootWindowContainer;
+
+ private final SparseArray<DisplayState> mDisplayStates = new SparseArray<>();
+
+ @NonNull private final ServiceDelegate mServiceDelegate = new ServiceDelegate();
KeyguardController(ActivityTaskManagerService service,
ActivityTaskSupervisor taskSupervisor) {
mService = service;
mTaskSupervisor = taskSupervisor;
- mSleepTokenAcquirer = mService.new SleepTokenAcquirerImpl(KEYGUARD_SLEEP_TOKEN_TAG);
+ mSleepTokenAcquirer = service.new SleepTokenAcquirerImpl(KEYGUARD_SLEEP_TOKEN_TAG);
}
void setWindowManager(WindowManagerService windowManager) {
mWindowManager = windowManager;
mRootWindowContainer = mService.mRootWindowContainer;
+ mService.getTransitionController().registerLegacyListener(
+ new WindowManagerInternal.AppTransitionListener() {
+ @Override
+ public int onAppTransitionStartingLocked(TransitionInfo info) {
+ final List<TransitionInfo.Change> changes = info.getChanges();
+ if (changes.size() == 0) {
+ Slog.e(TAG, "TransitionInfo doesn't contain change: " + info);
+ return 0;
+ }
+ final ActivityManager.RunningTaskInfo taskInfo =
+ changes.get(0).getTaskInfo();
+ if (taskInfo == null) {
+ Slog.e(TAG, "No RunningTaskInfo: " + info);
+ return 0;
+ }
+
+ // TODO(b/242856311): Filtering condition is defined here and in SysUI
+ // Keyguard service, which need to be in sync. For a long term, we should
+ // define a new API for notifying occlude status from WMS to SysUI, and
+ // the filtering logic should only exist in WM Shell.
+ if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) == 0) {
+ return 0;
+ }
+
+ boolean occludeOpeningApp = false;
+ boolean occludeClosingApp = false;
+ for (int i = 0; i < changes.size(); ++i) {
+ final TransitionInfo.Change change = changes.get(i);
+ if (change.hasFlags(FLAG_OCCLUDES_KEYGUARD)) {
+ if (change.getMode() == TRANSIT_OPEN
+ || change.getMode() == TRANSIT_TO_FRONT) {
+ occludeOpeningApp = true;
+ }
+ if (change.getMode() == TRANSIT_CLOSE
+ || change.getMode() == TRANSIT_TO_BACK) {
+ occludeClosingApp = true;
+ }
+ }
+ }
+ final DisplayState state = getDisplayState(taskInfo.displayId);
+ if (occludeOpeningApp && !occludeClosingApp) {
+ state.commitOccludedStatus(true /* occluded */);
+ } else if (!occludeOpeningApp && occludeClosingApp) {
+ state.commitOccludedStatus(false /* occluded */);
+ }
+ return 0;
+ }
+ });
}
boolean isAodShowing(int displayId) {
- return getDisplayState(displayId).mAodShowing;
+ return getDisplayState(displayId).isIn(KEYGUARD_STATE_AOD_SHOWN);
}
/**
- * @return true if either Keyguard or AOD are showing, not going away, and not being occluded
- * on the given display, false otherwise.
+ * @return {@code true} if either Keyguard or AOD are showing.
*/
boolean isKeyguardOrAodShowing(int displayId) {
- final KeyguardDisplayState state = getDisplayState(displayId);
- return (state.mKeyguardShowing || state.mAodShowing)
- && !state.mKeyguardGoingAway
- && !isDisplayOccluded(displayId);
+ return getDisplayState(displayId).isIn(KEYGUARD_STATE_KEYGUARD_TOP);
}
/**
- * @return {@code true} for default display when AOD is showing, not going away. Otherwise, same
- * as {@link #isKeyguardOrAodShowing(int)}
- * TODO(b/125198167): Replace isKeyguardOrAodShowing() by this logic.
+ * @return {@codd true} if lock screen is showing.
*/
- boolean isKeyguardUnoccludedOrAodShowing(int displayId) {
- final KeyguardDisplayState state = getDisplayState(displayId);
- if (displayId == DEFAULT_DISPLAY && state.mAodShowing) {
- return !state.mKeyguardGoingAway;
- }
- return isKeyguardOrAodShowing(displayId);
+ boolean isLocksScreenShowing(int displayId) {
+ // NOTE: This is only used by WindowManagerService#notifyKeyguardTrustedChanged
+ return getDisplayState(displayId).isIn(KEYGUARD_STATE_LOCKSCREEN_SHOWN);
}
/**
- * @return true if Keyguard is showing, not going away, and not being occluded on the given
- * display, false otherwise
- */
- boolean isKeyguardShowing(int displayId) {
- final KeyguardDisplayState state = getDisplayState(displayId);
- return state.mKeyguardShowing && !state.mKeyguardGoingAway
- && !isDisplayOccluded(displayId);
- }
-
- /**
- * @return true if Keyguard is either showing or occluded, but not going away
+ * @return {@code true} if Keyguard is either showing or occluded.
*/
boolean isKeyguardLocked(int displayId) {
- final KeyguardDisplayState state = getDisplayState(displayId);
- return state.mKeyguardShowing && !state.mKeyguardGoingAway;
+ return getDisplayState(displayId).isIn(KEYGUARD_STATE_ON);
}
/**
- *
* @return true if the activity is controlling keyguard state.
*/
boolean topActivityOccludesKeyguard(ActivityRecord r) {
- return getDisplayState(r.getDisplayId()).mTopOccludesActivity == r;
+ return getDisplayState(r.getDisplayId()).topActivityOccludesKeyguard(r);
}
/**
* @return {@code true} if the keyguard is going away, {@code false} otherwise.
*/
boolean isKeyguardGoingAway(int displayId) {
- final KeyguardDisplayState state = getDisplayState(displayId);
- // Also check keyguard showing in case value is stale.
- return state.mKeyguardGoingAway && state.mKeyguardShowing;
+ return getDisplayState(displayId).isIn(KEYGUARD_STATE_GOING_AWAY);
}
/**
- * Update the Keyguard showing state.
+ * Checks whether the top activity occludes the keyguard.
*/
- void setKeyguardShown(int displayId, boolean keyguardShowing, boolean aodShowing) {
- if (mRootWindowContainer.getDisplayContent(displayId).isKeyguardAlwaysUnlocked()) {
- Slog.i(TAG, "setKeyguardShown ignoring always unlocked display " + displayId);
- return;
- }
-
- final KeyguardDisplayState state = getDisplayState(displayId);
- final boolean aodChanged = aodShowing != state.mAodShowing;
- final boolean aodRemoved = state.mAodShowing && !aodShowing;
- // If keyguard is going away, but SystemUI aborted the transition, need to reset state.
- // Do not reset keyguardChanged status when only AOD is removed.
- final boolean keyguardChanged = (keyguardShowing != state.mKeyguardShowing)
- || (state.mKeyguardGoingAway && keyguardShowing && !aodRemoved);
- if (aodRemoved) {
- updateDeferTransitionForAod(false /* waiting */);
- }
- if (!keyguardChanged && !aodChanged) {
- setWakeTransitionReady();
- return;
- }
- EventLogTags.writeWmSetKeyguardShown(
- displayId,
- keyguardShowing ? 1 : 0,
- aodShowing ? 1 : 0,
- state.mKeyguardGoingAway ? 1 : 0,
- "setKeyguardShown");
-
- // Update the task snapshot if the screen will not be turned off. To make sure that the
- // unlocking animation can animate consistent content. The conditions are:
- // - Either AOD or keyguard changes to be showing. So if the states change individually,
- // the later one can be skipped to avoid taking snapshot again. While it still accepts
- // if both of them change to show at the same time.
- // - Keyguard was not going away. Because if it was, the closing transition is able to
- // handle the snapshot.
- // - The display state is ON. Because if AOD is not on or pulsing, the display state will
- // be OFF or DOZE (the path of screen off may have handled it).
- if (((aodShowing ^ keyguardShowing) || (aodShowing && aodChanged && keyguardChanged))
- && !state.mKeyguardGoingAway && Display.isOnState(
- mRootWindowContainer.getDefaultDisplay().getDisplayInfo().state)) {
- mWindowManager.mTaskSnapshotController.snapshotForSleeping(DEFAULT_DISPLAY);
- }
-
- state.mKeyguardShowing = keyguardShowing;
- state.mAodShowing = aodShowing;
-
- if (keyguardChanged) {
- // Irrelevant to AOD.
- state.mKeyguardGoingAway = false;
- if (keyguardShowing) {
- state.mDismissalRequested = false;
- }
- }
-
- // Update the sleep token first such that ensureActivitiesVisible has correct sleep token
- // state when evaluating visibilities.
- updateKeyguardSleepToken();
- mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
- InputMethodManagerInternal.get().updateImeWindowStatus(false /* disableImeIcon */);
- setWakeTransitionReady();
- if (aodChanged) {
- // Ensure the new state takes effect.
- mWindowManager.mWindowPlacerLocked.performSurfacePlacement();
- }
- }
-
- private void setWakeTransitionReady() {
- if (mWindowManager.mAtmService.getTransitionController().getCollectingTransitionType()
- == WindowManager.TRANSIT_WAKE) {
- mWindowManager.mAtmService.getTransitionController().setReady(
- mRootWindowContainer.getDefaultDisplay());
- }
+ boolean isDisplayOccluded(int displayId) {
+ return getDisplayState(displayId).isIn(KEYGUARD_STATE_OCCLUDED);
}
/**
- * Called when Keyguard is going away.
- *
- * @param flags See {@link WindowManagerPolicy#KEYGUARD_GOING_AWAY_FLAG_TO_SHADE}
- * etc.
+ * @return Whether the dream activity is on top of default display.
*/
- void keyguardGoingAway(int displayId, int flags) {
- final KeyguardDisplayState state = getDisplayState(displayId);
- if (!state.mKeyguardShowing || state.mKeyguardGoingAway) {
- return;
- }
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "keyguardGoingAway");
- mService.deferWindowLayout();
- state.mKeyguardGoingAway = true;
- try {
- EventLogTags.writeWmSetKeyguardShown(
- displayId,
- 1 /* keyguardShowing */,
- state.mAodShowing ? 1 : 0,
- 1 /* keyguardGoingAway */,
- "keyguardGoingAway");
- final int transitFlags = convertTransitFlags(flags);
- final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
- dc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, transitFlags);
- // We are deprecating TRANSIT_KEYGUARD_GOING_AWAY for Shell transition and use
- // TRANSIT_FLAG_KEYGUARD_GOING_AWAY to indicate that it should animate keyguard going
- // away.
- dc.mAtmService.getTransitionController().requestTransitionIfNeeded(
- TRANSIT_TO_BACK, transitFlags, null /* trigger */, dc);
- updateKeyguardSleepToken();
-
- // Some stack visibility might change (e.g. docked stack)
- mRootWindowContainer.resumeFocusedTasksTopActivities();
- mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
- mRootWindowContainer.addStartingWindowsForVisibleActivities();
- mWindowManager.executeAppTransition();
- } finally {
- mService.continueWindowLayout();
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- }
- }
-
- void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback, CharSequence message) {
- final ActivityRecord activityRecord = ActivityRecord.forTokenLocked(token);
- if (activityRecord == null || !activityRecord.visibleIgnoringKeyguard) {
- failCallback(callback);
- return;
- }
- Slog.i(TAG, "Activity requesting to dismiss Keyguard: " + activityRecord);
-
- // If the client has requested to dismiss the keyguard and the Activity has the flag to
- // turn the screen on, wakeup the screen if it's the top Activity.
- if (activityRecord.getTurnScreenOnFlag() && activityRecord.isTopRunningActivity()) {
- mTaskSupervisor.wakeUp("dismissKeyguard");
- }
-
- mWindowManager.dismissKeyguard(callback, message);
- }
-
- private void failCallback(IKeyguardDismissCallback callback) {
- try {
- callback.onDismissError();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to call callback", e);
- }
- }
-
- private int convertTransitFlags(int keyguardGoingAwayFlags) {
- int result = TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
- if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_TO_SHADE) != 0) {
- result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
- }
- if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS) != 0) {
- result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
- }
- if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER) != 0) {
- result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
- }
- if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS) != 0) {
- result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
- }
- if ((keyguardGoingAwayFlags
- & KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT) != 0) {
- result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT;
- }
- return result;
- }
-
- /**
- * @return True if we may show an activity while Keyguard is showing because we are in the
- * process of dismissing it anyways, false otherwise.
- */
- boolean canShowActivityWhileKeyguardShowing(ActivityRecord r) {
- // Allow to show it when we are about to dismiss Keyguard. This isn't allowed if r is
- // already the dismissing activity, in which case we don't allow it to repeatedly dismiss
- // Keyguard.
- final KeyguardDisplayState state = getDisplayState(r.getDisplayId());
- return r.containsDismissKeyguardWindow() && canDismissKeyguard() && !state.mAodShowing
- && (state.mDismissalRequested
- || (r.canShowWhenLocked() && state.mDismissingKeyguardActivity != r));
- }
-
- /**
- * @return True if we may show an activity while Keyguard is occluded, false otherwise.
- */
- boolean canShowWhileOccluded(boolean dismissKeyguard, boolean showWhenLocked) {
- return showWhenLocked || dismissKeyguard
- && !mWindowManager.isKeyguardSecure(mService.getCurrentUserId());
+ boolean isShowingDream() {
+ return getDisplayState(DEFAULT_DISPLAY).isIn(KEYGUARD_STATE_DREAMING);
}
/**
@@ -351,181 +224,15 @@
*
* @return true if {@param r} is visible taken Keyguard state into account, false otherwise
*/
- boolean checkKeyguardVisibility(ActivityRecord r) {
+ boolean checkKeyguardVisibility(@NonNull ActivityRecord r) {
if (r.mDisplayContent.canShowWithInsecureKeyguard() && canDismissKeyguard()) {
return true;
}
-
- if (isKeyguardOrAodShowing(r.mDisplayContent.getDisplayId())) {
- // If keyguard is showing, nothing is visible, except if we are able to dismiss Keyguard
- // right away and AOD isn't visible.
- return canShowActivityWhileKeyguardShowing(r);
- } else if (isKeyguardLocked(r.getDisplayId())) {
- return canShowWhileOccluded(r.containsDismissKeyguardWindow(), r.canShowWhenLocked());
- } else {
- return true;
- }
- }
-
- /**
- * Makes sure to update lockscreen occluded/dismiss/turnScreenOn state if needed before
- * completing set all visibility
- * ({@link ActivityTaskSupervisor#beginActivityVisibilityUpdate}).
- */
- void updateVisibility() {
- for (int displayNdx = mRootWindowContainer.getChildCount() - 1;
- displayNdx >= 0; displayNdx--) {
- final DisplayContent display = mRootWindowContainer.getChildAt(displayNdx);
- if (display.isRemoving() || display.isRemoved()) continue;
- final KeyguardDisplayState state = getDisplayState(display.mDisplayId);
- state.updateVisibility(this, display);
- if (state.mRequestDismissKeyguard) {
- handleDismissKeyguard(display.getDisplayId());
- }
- }
- }
-
- /**
- * Called when occluded state changed.
- *
- * @param topActivity the activity that controls the state whether keyguard should
- * be occluded. That is the activity to be shown on top of keyguard if it requests so.
- */
- private void handleOccludedChanged(int displayId, @Nullable ActivityRecord topActivity) {
- // TODO(b/113840485): Handle app transition for individual display, and apply occluded
- // state change to secondary displays.
- // For now, only default display fully supports occluded change. Other displays only
- // updates keyguard sleep token on that display.
- if (displayId != DEFAULT_DISPLAY) {
- updateKeyguardSleepToken(displayId);
- return;
- }
-
- mWindowManager.mPolicy.onKeyguardOccludedChangedLw(isDisplayOccluded(DEFAULT_DISPLAY));
- if (isKeyguardLocked(displayId)) {
- mService.deferWindowLayout();
- try {
- mRootWindowContainer.getDefaultDisplay()
- .requestTransitionAndLegacyPrepare(
- isDisplayOccluded(DEFAULT_DISPLAY)
- ? TRANSIT_KEYGUARD_OCCLUDE
- : TRANSIT_KEYGUARD_UNOCCLUDE, 0 /* flags */);
- updateKeyguardSleepToken(DEFAULT_DISPLAY);
- mWindowManager.executeAppTransition();
- } finally {
- mService.continueWindowLayout();
- }
- }
- dismissMultiWindowModeForTaskIfNeeded(displayId, topActivity != null
- ? topActivity.getRootTask() : null);
- }
-
- /**
- * Called when keyguard going away state changed.
- */
- private void handleKeyguardGoingAwayChanged(DisplayContent dc) {
- mService.deferWindowLayout();
- try {
- dc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, 0 /* transitFlags */);
- // We are deprecating TRANSIT_KEYGUARD_GOING_AWAY for Shell transition and use
- // TRANSIT_FLAG_KEYGUARD_GOING_AWAY to indicate that it should animate keyguard going
- // away.
- dc.mAtmService.getTransitionController().requestTransitionIfNeeded(
- TRANSIT_OPEN, TRANSIT_FLAG_KEYGUARD_GOING_AWAY, null /* trigger */, dc);
- updateKeyguardSleepToken();
- mWindowManager.executeAppTransition();
- } finally {
- mService.continueWindowLayout();
- }
- }
-
- /**
- * Called when somebody wants to dismiss the Keyguard via the flag.
- */
- private void handleDismissKeyguard(int displayId) {
- // We only allow dismissing Keyguard via the flag when Keyguard is secure for legacy
- // reasons, because that's how apps used to dismiss Keyguard in the secure case. In the
- // insecure case, we actually show it on top of the lockscreen. See #canShowWhileOccluded.
- if (!mWindowManager.isKeyguardSecure(mService.getCurrentUserId())) {
- return;
- }
-
- mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
- final KeyguardDisplayState state = getDisplayState(displayId);
- state.mDismissalRequested = true;
-
- // If we are about to unocclude the Keyguard, but we can dismiss it without security,
- // we immediately dismiss the Keyguard so the activity gets shown without a flicker.
- final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
- if (state.mKeyguardShowing && canDismissKeyguard()
- && dc.mAppTransition.containsTransitRequest(TRANSIT_KEYGUARD_UNOCCLUDE)) {
- mWindowManager.executeAppTransition();
- }
- }
-
- boolean isDisplayOccluded(int displayId) {
- return getDisplayState(displayId).mOccluded;
- }
-
- /**
- * @return true if Keyguard can be currently dismissed without entering credentials.
- */
- boolean canDismissKeyguard() {
- return mWindowManager.mPolicy.isKeyguardTrustedLw()
- || !mWindowManager.isKeyguardSecure(mService.getCurrentUserId());
- }
-
- /**
- * @return Whether the dream activity is on top of default display.
- */
- boolean isShowingDream() {
- return getDisplayState(DEFAULT_DISPLAY).mShowingDream;
- }
-
- private void dismissMultiWindowModeForTaskIfNeeded(int displayId,
- @Nullable Task currentTaskControllingOcclusion) {
- // TODO(b/113840485): Handle docked stack for individual display.
- if (!getDisplayState(displayId).mKeyguardShowing || !isDisplayOccluded(DEFAULT_DISPLAY)) {
- return;
- }
-
- // Dismiss freeform windowing mode
- if (currentTaskControllingOcclusion == null) {
- return;
- }
- if (currentTaskControllingOcclusion.inFreeformWindowingMode()) {
- currentTaskControllingOcclusion.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- }
- }
-
- private void updateKeyguardSleepToken() {
- for (int displayNdx = mRootWindowContainer.getChildCount() - 1;
- displayNdx >= 0; displayNdx--) {
- final DisplayContent display = mRootWindowContainer.getChildAt(displayNdx);
- updateKeyguardSleepToken(display.mDisplayId);
- }
- }
-
- private void updateKeyguardSleepToken(int displayId) {
- final KeyguardDisplayState state = getDisplayState(displayId);
- if (isKeyguardUnoccludedOrAodShowing(displayId)) {
- state.mSleepTokenAcquirer.acquire(displayId);
- } else {
- state.mSleepTokenAcquirer.release(displayId);
- }
- }
-
- private KeyguardDisplayState getDisplayState(int displayId) {
- KeyguardDisplayState state = mDisplayStates.get(displayId);
- if (state == null) {
- state = new KeyguardDisplayState(mService, displayId, mSleepTokenAcquirer);
- mDisplayStates.append(displayId, state);
- }
- return state;
+ return getDisplayState(r.mDisplayContent.getDisplayId()).checkKeyguardVisibility(r);
}
void onDisplayRemoved(int displayId) {
- final KeyguardDisplayState state = mDisplayStates.get(displayId);
+ final DisplayState state = mDisplayStates.get(displayId);
if (state != null) {
state.onRemoved();
mDisplayStates.remove(displayId);
@@ -538,7 +245,15 @@
}
};
- // Defer transition until AOD dismissed.
+ /**
+ * Update if app transition should be deferred until AOD state changes.
+ *
+ * <p>Note: This is used for defer app transition before the device fully wakes up, since during
+ * wake up process, activities life cycle can be messed up due to a display sleep token.
+ *
+ * @param waiting {@code true} to defer an app transition, {@code false} to continue an app
+ * transition.
+ */
void updateDeferTransitionForAod(boolean waiting) {
if (waiting == mWaitingForWakeTransition) {
return;
@@ -549,212 +264,1115 @@
// if AOD is showing, defer the wake transition until AOD state changed.
if (waiting && isAodShowing(DEFAULT_DISPLAY)) {
mWaitingForWakeTransition = true;
- mWindowManager.mAtmService.getTransitionController().deferTransitionReady();
+ mService.getTransitionController().deferTransitionReady();
mWindowManager.mH.postDelayed(mResetWaitTransition, DEFER_WAKE_TRANSITION_TIMEOUT_MS);
} else if (!waiting) {
// dismiss the deferring if the AOD state change or cancel awake.
mWaitingForWakeTransition = false;
- mWindowManager.mAtmService.getTransitionController().continueTransitionReady();
+ mService.getTransitionController().continueTransitionReady();
mWindowManager.mH.removeCallbacks(mResetWaitTransition);
}
}
-
- /** Represents Keyguard state per individual display. */
- private static class KeyguardDisplayState {
- private final int mDisplayId;
- private boolean mKeyguardShowing;
- private boolean mAodShowing;
- private boolean mKeyguardGoingAway;
- private boolean mDismissalRequested;
- private boolean mOccluded;
- private boolean mShowingDream;
-
- private ActivityRecord mTopOccludesActivity;
- private ActivityRecord mDismissingKeyguardActivity;
- private ActivityRecord mTopTurnScreenOnActivity;
-
- private boolean mRequestDismissKeyguard;
- private final ActivityTaskManagerService mService;
- private final ActivityTaskManagerInternal.SleepTokenAcquirer mSleepTokenAcquirer;
-
- KeyguardDisplayState(ActivityTaskManagerService service, int displayId,
- ActivityTaskManagerInternal.SleepTokenAcquirer acquirer) {
- mService = service;
- mDisplayId = displayId;
- mSleepTokenAcquirer = acquirer;
- }
-
- void onRemoved() {
- mTopOccludesActivity = null;
- mDismissingKeyguardActivity = null;
- mTopTurnScreenOnActivity = null;
- mSleepTokenAcquirer.release(mDisplayId);
- }
-
- /**
- * Updates keyguard status if the top task could be visible. The top task may occlude
- * keyguard, request to dismiss keyguard or make insecure keyguard go away based on its
- * properties.
- */
- void updateVisibility(KeyguardController controller, DisplayContent display) {
- final boolean lastOccluded = mOccluded;
- final boolean lastKeyguardGoingAway = mKeyguardGoingAway;
-
- final ActivityRecord lastDismissKeyguardActivity = mDismissingKeyguardActivity;
- final ActivityRecord lastTurnScreenOnActivity = mTopTurnScreenOnActivity;
-
- mRequestDismissKeyguard = false;
- mOccluded = false;
- mShowingDream = false;
-
- mTopOccludesActivity = null;
- mDismissingKeyguardActivity = null;
- mTopTurnScreenOnActivity = null;
-
- boolean occludedByActivity = false;
- final Task task = getRootTaskForControllingOccluding(display);
- final ActivityRecord top = task != null ? task.getTopNonFinishingActivity() : null;
- if (top != null) {
- if (top.containsDismissKeyguardWindow()) {
- mDismissingKeyguardActivity = top;
- }
- if (top.getTurnScreenOnFlag() && top.currentLaunchCanTurnScreenOn()) {
- mTopTurnScreenOnActivity = top;
- }
-
- if (top.mDismissKeyguard && mKeyguardShowing) {
- mKeyguardGoingAway = true;
- } else if (top.canShowWhenLocked()) {
- mTopOccludesActivity = top;
- }
- top.mDismissKeyguard = false;
-
- // Only the top activity may control occluded, as we can't occlude the Keyguard
- // if the top app doesn't want to occlude it.
- occludedByActivity = mTopOccludesActivity != null
- || (mDismissingKeyguardActivity != null
- && task.topRunningActivity() == mDismissingKeyguardActivity
- && controller.canShowWhileOccluded(
- true /* dismissKeyguard */, false /* showWhenLocked */));
- // FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD only apply for secondary display.
- if (mDisplayId != DEFAULT_DISPLAY) {
- occludedByActivity |= display.canShowWithInsecureKeyguard()
- && controller.canDismissKeyguard();
- }
+ /**
+ * TODO(b/242851358): Remove this function once SysUI migrate to the new API.
+ */
+ @KeyguardState private static int convertToState(int displayId, boolean keyguardShowing,
+ boolean aodShowing) {
+ if (displayId == DEFAULT_DISPLAY) {
+ if (aodShowing) {
+ return KEYGUARD_STATE_AOD_SHOWN;
+ } else if (keyguardShowing) {
+ return KEYGUARD_STATE_LOCKSCREEN_SHOWN;
+ } else {
+ return KEYGUARD_STATE_OFF;
}
-
- mShowingDream = display.getDisplayPolicy().isShowingDreamLw() && (top != null
- && top.getActivityType() == ACTIVITY_TYPE_DREAM);
- mOccluded = mShowingDream || occludedByActivity;
- mRequestDismissKeyguard = lastDismissKeyguardActivity != mDismissingKeyguardActivity
- && !mOccluded && !mKeyguardGoingAway
- && mDismissingKeyguardActivity != null;
- if (mOccluded && mKeyguardShowing && !display.isSleeping() && !top.fillsParent()
- && display.mWallpaperController.getWallpaperTarget() == null) {
- // The occluding activity may be translucent or not fill screen. Then let wallpaper
- // to check whether it should set itself as target to avoid blank background.
- display.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ } else {
+ if (keyguardShowing || aodShowing) {
+ return KEYGUARD_STATE_LOCKSCREEN_SHOWN;
+ } else {
+ return KEYGUARD_STATE_OFF;
}
-
- if (mTopTurnScreenOnActivity != lastTurnScreenOnActivity
- && mTopTurnScreenOnActivity != null
- && !mService.mWindowManager.mPowerManager.isInteractive()
- && (mRequestDismissKeyguard || occludedByActivity)) {
- controller.mTaskSupervisor.wakeUp("handleTurnScreenOn");
- mTopTurnScreenOnActivity.setCurrentLaunchCanTurnScreenOn(false);
- }
-
- boolean hasChange = false;
- if (lastOccluded != mOccluded) {
- controller.handleOccludedChanged(mDisplayId, mTopOccludesActivity);
- hasChange = true;
- } else if (!lastKeyguardGoingAway && mKeyguardGoingAway) {
- controller.handleKeyguardGoingAwayChanged(display);
- hasChange = true;
- }
- // Collect the participates for shell transition, so that transition won't happen too
- // early since the transition was set ready.
- if (hasChange && top != null && (mOccluded || mKeyguardGoingAway)) {
- display.mTransitionController.collect(top);
- }
- }
-
- /**
- * Gets the stack used to check the occluded state.
- * <p>
- * Only the top non-pinned activity of the focusable stack on each display can control its
- * occlusion state.
- */
- @Nullable
- private Task getRootTaskForControllingOccluding(DisplayContent display) {
- return display.getRootTask(task ->
- task != null && task.isFocusableAndVisible() && !task.inPinnedWindowingMode());
- }
-
- void dumpStatus(PrintWriter pw, String prefix) {
- final StringBuilder sb = new StringBuilder();
- sb.append(prefix);
- sb.append(" KeyguardShowing=")
- .append(mKeyguardShowing)
- .append(" AodShowing=")
- .append(mAodShowing)
- .append(" KeyguardGoingAway=")
- .append(mKeyguardGoingAway)
- .append(" DismissalRequested=")
- .append(mDismissalRequested)
- .append(" Occluded=")
- .append(mOccluded)
- .append(" DismissingKeyguardActivity=")
- .append(mDismissingKeyguardActivity)
- .append(" TurnScreenOnActivity=")
- .append(mTopTurnScreenOnActivity)
- .append(" at display=")
- .append(mDisplayId);
- pw.println(sb.toString());
- }
-
- void dumpDebug(ProtoOutputStream proto, long fieldId) {
- final long token = proto.start(fieldId);
- proto.write(KeyguardPerDisplayProto.DISPLAY_ID, mDisplayId);
- proto.write(KeyguardPerDisplayProto.KEYGUARD_SHOWING, mKeyguardShowing);
- proto.write(KeyguardPerDisplayProto.AOD_SHOWING, mAodShowing);
- proto.write(KeyguardPerDisplayProto.KEYGUARD_OCCLUDED, mOccluded);
- proto.write(KeyguardPerDisplayProto.KEYGUARD_GOING_AWAY, mKeyguardGoingAway);
- proto.end(token);
}
}
- void dump(PrintWriter pw, String prefix) {
- final KeyguardDisplayState default_state = getDisplayState(DEFAULT_DISPLAY);
- pw.println(prefix + "KeyguardController:");
- pw.println(prefix + " mKeyguardShowing=" + default_state.mKeyguardShowing);
- pw.println(prefix + " mAodShowing=" + default_state.mAodShowing);
- pw.println(prefix + " mKeyguardGoingAway=" + default_state.mKeyguardGoingAway);
- dumpDisplayStates(pw, prefix);
- pw.println(prefix + " mDismissalRequested=" + default_state.mDismissalRequested);
- pw.println();
+ /**
+ * Update the Keyguard showing state.
+ *
+ * @deprecated Use {@link #setKeyguardState(int, int)} instead. See b/242851358
+ */
+ @Deprecated
+ void setKeyguardShown(int displayId, boolean keyguardShowing, boolean aodShowing) {
+ final DisplayState state = getDisplayState(displayId);
+ EventLogTags.writeWmSetKeyguardShown(
+ displayId,
+ keyguardShowing ? 1 : 0,
+ aodShowing ? 1 : 0,
+ state.isIn(KEYGUARD_STATE_GOING_AWAY) ? 1 : 0,
+ "setKeyguardShown");
+ setKeyguardState(displayId, convertToState(displayId, keyguardShowing, aodShowing));
+ }
+
+ /**
+ * Set keyguard state.
+ */
+ private void setKeyguardState(int displayId, @KeyguardState int newState) {
+ if (mRootWindowContainer.getDisplayContent(displayId).isKeyguardAlwaysUnlocked()) {
+ Slog.i(TAG, "setKeyguardShown ignoring always unlocked display " + displayId);
+ return;
+ }
+ if (newState != KEYGUARD_STATE_LOCKSCREEN_SHOWN
+ && newState != KEYGUARD_STATE_AOD_SHOWN
+ && newState != KEYGUARD_STATE_OFF
+ && newState != KEYGUARD_STATE_GOING_AWAY) {
+ Slog.i(TAG, "Invalid state is requested: displayId=" + displayId
+ + ", state=" + keyguardStateToString(newState)
+ + ", stack=" + Debug.getCallers(30));
+ return;
+ }
+ if (isKeyguardLocked(displayId) && newState == KEYGUARD_STATE_OFF) {
+ newState = KEYGUARD_STATE_GOING_AWAY;
+ }
+
+ final DisplayState state = getDisplayState(displayId);
+ // SysUI requests to show LOCKSCREEN, but the keyguard is already occluded. Ignore the
+ // requests.
+ if (state.isIn(KEYGUARD_STATE_OCCLUDED)
+ && StateMachine.isIn(newState, KEYGUARD_STATE_LOCKSCREEN_SHOWN)) {
+ Slog.w(TAG, "Ignore setKeyguardState request: OCCLUDE -> LOCK_SCREEN_SHOWN");
+ return;
+ }
+ // SysUI requests to show AOD_SHOWN again. This can happen when SysUI still uses the old
+ // API and enables AOD first, then lock screen, i.e. #setLockScreenShown(false, true), then
+ // #setLockScreenShown(true, true)
+ if (state.isIn(KEYGUARD_STATE_AOD_SHOWN)
+ && StateMachine.isIn(newState, KEYGUARD_STATE_AOD_SHOWN)) {
+ Slog.w(TAG, "Ignore setKeyguardState request: AOD_SHOWN -> AOD_SHOWN");
+ return;
+ }
+ if (state.isIn(KEYGUARD_STATE_OFF)
+ && StateMachine.isIn(newState, KEYGUARD_STATE_GOING_AWAY)) {
+ Slog.w(TAG, "Ignore setKeyguardState request: OFF -> GOING_AWAY");
+ return;
+ }
+ if (state.isIn(KEYGUARD_STATE_AOD_SHOWN)
+ && StateMachine.isIn(newState, KEYGUARD_STATE_LOCKSCREEN_SHOWN)) {
+ ActivityRecord top = getTopNonFinishingActivity(displayId);
+ if (canOcclude(top)) {
+ newState = isTopActivityDreaming(displayId) ? KEYGUARD_STATE_DREAMING
+ : KEYGUARD_STATE_OCCLUDED;
+ }
+ }
+ state.setKeyguardState(newState);
+ }
+
+ /**
+ * Called when Keyguard is going away.
+ *
+ * @param flags See {@link WindowManagerPolicy#KEYGUARD_GOING_AWAY_FLAG_TO_SHADE}
+ * etc.
+ *
+ * @deprecated Use {@link #setKeyguardState(int, int)}
+ */
+ void keyguardGoingAway(int displayId, int flags) {
+ // TODO(b/242851358): Remove IActivityTaskManagerService#keyguardGoingAway and SysUI should
+ // request the state change via #setKeyguardState.
+ final DisplayState state = getDisplayState(displayId);
+ EventLogTags.writeWmSetKeyguardShown(
+ displayId,
+ state.isIn(KEYGUARD_STATE_LOCKSCREEN_SHOWN) ? 1 : 0,
+ state.isIn(KEYGUARD_STATE_AOD_SHOWN) ? 1 : 0,
+ 1 /* keyguardGoingAway */,
+ "keyguardGoingAway");
+ setKeyguardState(displayId, KEYGUARD_STATE_GOING_AWAY);
+ }
+
+ /**
+ * Makes sure to update lockscreen state if needed before completing set all visibility
+ * ({@link ActivityTaskSupervisor#beginActivityVisibilityUpdate}).
+ */
+ void updateVisibility() {
+ for (int displayNdx = mRootWindowContainer.getChildCount() - 1;
+ displayNdx >= 0; displayNdx--) {
+ final DisplayContent display = mRootWindowContainer.getChildAt(displayNdx);
+ if (display.isRemoving() || display.isRemoved()) continue;
+ final DisplayState state = getDisplayState(display.mDisplayId);
+ state.updateVisibility();
+ }
+ }
+
+ void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback, CharSequence message) {
+ boolean ok;
+ final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+ if (r == null) {
+ ok = false;
+ } else {
+ final DisplayState state = getDisplayState(r.getDisplayId());
+ ok = state.dismissKeyguard(r, callback, message);
+ }
+
+ if (!ok) {
+ try {
+ callback.onDismissError();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call callback", e);
+ }
+ }
+ }
+
+ private boolean isKeyguardSecure() {
+ return mWindowManager.isKeyguardSecure(mService.getCurrentUserId());
+ }
+
+ /**
+ * @return true if Keyguard can be currently dismissed without entering credentials.
+ */
+ private boolean canDismissKeyguard() {
+ return mWindowManager.mPolicy.isKeyguardTrustedLw() || !isKeyguardSecure();
+ }
+
+ private boolean canOcclude(@Nullable ActivityRecord r) {
+ if (r == null) {
+ return false;
+ }
+ // FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD only apply for secondary display.
+ if (r.getDisplayId() != DEFAULT_DISPLAY) {
+ final DisplayContent dc = r.getDisplayContent();
+ if (dc != null && dc.canShowWithInsecureKeyguard() && canDismissKeyguard()) {
+ return true;
+ }
+ }
+ // FLAG_DISMISS_KEYGUARD activity
+ // Insecure: Treat as FLAG_SHOW_WHEN_LOCKED
+ // Trusted: Actually dismiss Keyguard.
+ // Secure: Show bouncer.
+ return r.canShowWhenLocked() || (r.containsDismissKeyguardWindow() && !isKeyguardSecure());
+ }
+
+ private boolean isTopActivityDreaming(int displayId) {
+ final DisplayContent dc = mRootWindowContainer.getDisplayContent(displayId);
+ final ActivityRecord top = getTopNonFinishingActivity(displayId);
+ return dc.getDisplayPolicy().isShowingDreamLw()
+ && top != null && top.getActivityType() == ACTIVITY_TYPE_DREAM;
+ }
+
+ @Nullable private ActivityRecord getTopNonFinishingActivity(int displayId) {
+ final DisplayContent dc = mRootWindowContainer.getDisplayContent(displayId);
+ final Task rootTask = dc == null ? null : dc.getRootTask(t ->
+ t != null && t.isFocusableAndVisible() && !t.inPinnedWindowingMode());
+ return rootTask != null ? rootTask.getTopNonFinishingActivity() : null;
+ }
+
+ private DisplayState getDisplayState(int displayId) {
+ DisplayState state = mDisplayStates.get(displayId);
+ if (state == null) {
+ state = new DisplayState(displayId, mServiceDelegate);
+ mDisplayStates.append(displayId, state);
+ }
+ return state;
}
void dumpDebug(ProtoOutputStream proto, long fieldId) {
- final KeyguardDisplayState default_state = getDisplayState(DEFAULT_DISPLAY);
+ final DisplayState default_state = getDisplayState(DEFAULT_DISPLAY);
final long token = proto.start(fieldId);
- proto.write(AOD_SHOWING, default_state.mAodShowing);
- proto.write(KEYGUARD_SHOWING, default_state.mKeyguardShowing);
- proto.write(KEYGUARD_GOING_AWAY, default_state.mKeyguardGoingAway);
+ proto.write(AOD_SHOWING, default_state.isIn(KEYGUARD_STATE_AOD_SHOWN));
+ proto.write(KEYGUARD_SHOWING, default_state.isIn(KEYGUARD_STATE_LOCKSCREEN_SHOWN));
writeDisplayStatesToProto(proto, KEYGUARD_PER_DISPLAY);
proto.end(token);
}
- private void dumpDisplayStates(PrintWriter pw, String prefix) {
- for (int i = 0; i < mDisplayStates.size(); i++) {
- mDisplayStates.valueAt(i).dumpStatus(pw, prefix);
- }
- }
-
private void writeDisplayStatesToProto(ProtoOutputStream proto, long fieldId) {
for (int i = 0; i < mDisplayStates.size(); i++) {
mDisplayStates.valueAt(i).dumpDebug(proto, fieldId);
}
}
+
+ void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix);
+ pw.println("KeyguardController:");
+ for (int i = 0; i < mDisplayStates.size(); i++) {
+ mDisplayStates.valueAt(i).dump(pw, prefix);
+ }
+ pw.println();
+ }
+
+ /**
+ * Interface for {@link DisplayState} to access non-local information.
+ * <p>
+ * Keep this interface as small as possible, and don't let {@link DisplayState} access arbitrary
+ * large classes such as ActivityTaskSupervisor, which makes managing dependency complicated.
+ */
+ private final class ServiceDelegate {
+ boolean isKeyguardSecure() {
+ return KeyguardController.this.isKeyguardSecure();
+ }
+
+ boolean canOcclude(@Nullable ActivityRecord r) {
+ return KeyguardController.this.canOcclude(r);
+ }
+
+ boolean canDismissKeyguard() {
+ return KeyguardController.this.canDismissKeyguard();
+ }
+
+ boolean isDeviceInteractive() {
+ return mService.mWindowManager.mPowerManager.isInteractive();
+ }
+
+ void dismissKeyguard(IKeyguardDismissCallback callback, CharSequence message) {
+ mWindowManager.dismissKeyguard(callback, message);
+ }
+
+ @Nullable
+ ActivityRecord getTopNonFinishingActivity(int displayId) {
+ return KeyguardController.this.getTopNonFinishingActivity(displayId);
+ }
+
+ boolean isTopActivityDreaming(int displayId) {
+ return KeyguardController.this.isTopActivityDreaming(displayId);
+ }
+
+ void wakeUp(String reason) {
+ mTaskSupervisor.wakeUp(reason);
+ }
+
+ void forceSyncOccludedStatus(boolean occluded) {
+ if (DEBUG) {
+ Slog.d(TAG, "forceSyncOccludedStatus: occluded=" + occluded);
+ }
+ mWindowManager.mPolicy.onKeyguardOccludedChangedLw(occluded);
+ mWindowManager.mPolicy.applyKeyguardOcclusionChange(true /* notify */);
+ }
+
+ void snapshotForSleeping(int displayId) {
+ if (displayId == DEFAULT_DISPLAY) {
+ mWindowManager.mTaskSnapshotController.snapshotForSleeping(displayId);
+ }
+ }
+
+ void notifyKeyguardOccludeChanged(boolean occluded) {
+ // TODO: This updates status of KeyguardDelegate. Once we delete occlude status from
+ // KeyguardDelegate, we should remove WindowManagerPolicy#onKeyguardOccludedChangedLw.
+ mWindowManager.mPolicy.onKeyguardOccludedChangedLw(occluded);
+ }
+
+ void collect(@NonNull WindowContainer wc) {
+ mService.getTransitionController().collect(wc);
+ }
+
+ void requestTransitionIfNeeded(int displayId, @WindowManager.TransitionType int transit,
+ @WindowManager.TransitionFlags int flags) {
+ if (displayId != DEFAULT_DISPLAY) {
+ return;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "requestTransitionIfNeeded: display=" + displayId + ", transit="
+ + transitTypeToString(transit));
+ }
+
+ final DisplayContent dc = mRootWindowContainer.getDisplayContent(displayId);
+ if (dc == null) {
+ Slog.e(TAG, "No DisplayContent exists: displayId=" + displayId);
+ return;
+ }
+
+ if (transit == TRANSIT_KEYGUARD_GOING_AWAY) {
+ dc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, flags);
+ // We are deprecating TRANSIT_KEYGUARD_GOING_AWAY for Shell transition and use
+ // TRANSIT_FLAG_KEYGUARD_GOING_AWAY to indicate that it should animate keyguard
+ // going away.
+ mService.getTransitionController().requestTransitionIfNeeded(
+ TRANSIT_TO_BACK, flags, null /* trigger */, dc);
+ } else {
+ dc.requestTransitionAndLegacyPrepare(transit, flags);
+ }
+ }
+
+ void acquireSleepToken(int displayId, boolean ensureActivitiesVisible) {
+ mSleepTokenAcquirer.acquire(displayId);
+ if (ensureActivitiesVisible) {
+ mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ }
+ }
+
+ void releaseSleepToken(int displayId, boolean resumeTopActivities) {
+ mSleepTokenAcquirer.release(displayId);
+ if (resumeTopActivities) {
+ mRootWindowContainer.resumeFocusedTasksTopActivities();
+ mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ mRootWindowContainer.addStartingWindowsForVisibleActivities();
+
+ }
+ }
+
+ void deferWindowLayout() {
+ mService.deferWindowLayout();
+ }
+
+ void continueWindowLayout() {
+ mService.continueWindowLayout();
+ }
+
+ void executeAppTransition() {
+ mWindowManager.executeAppTransition();
+ }
+
+ private void updateDeferTransitionForAod(boolean waiting) {
+ KeyguardController.this.updateDeferTransitionForAod(waiting);
+ }
+
+ private void setWakeTransitionReady() {
+ if (mService.getTransitionController().getCollectingTransitionType() == TRANSIT_WAKE) {
+ mService.getTransitionController().setReady(
+ mRootWindowContainer.getDefaultDisplay());
+ }
+ }
+
+ void requestLayoutRedoWallpaper(int displayId) {
+ final DisplayContent dc = mRootWindowContainer.getDisplayContent(displayId);
+ if (!dc.isSleeping() && dc.mWallpaperController.getWallpaperTarget() != null) {
+ dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ }
+ }
+ };
+
+ private static class KeyguardDisplayStateMachine extends StateMachine {
+ static final int EVENT_DISMISS_KEYGUARD_ACTIVITY = 1;
+ static final int EVENT_SHOW_WHEN_LOCKED_ACTIVITY = 2;
+ static final int EVENT_CHECK_KEYGUARD_VISIBILITY = 3;
+ static final int EVENT_TOP_ACTIVITY_OCCLUDES_KEYGUARD = 4;
+ static final int EVENT_LAYOUT_CHANGES = 5;
+ static final int EVENT_DUMP = 6;
+ static final int EVENT_DISMISS_KEYGUARD_API = 7;
+ static final int EVENT_TURN_SCREEN_ON_ACTIVITY = 8;
+ final int mDisplayId;
+
+ static final class CheckKeyguardVisibilityParam {
+ boolean mRet;
+ @NonNull final ActivityRecord mActivity;
+
+ CheckKeyguardVisibilityParam(@NonNull ActivityRecord activity) {
+ mActivity = activity;
+ }
+ }
+
+ static final class DismissKeyguardParam {
+ boolean mRet;
+ @NonNull final ActivityRecord mActivity;
+ @Nullable final IKeyguardDismissCallback mCallback;
+ @Nullable final CharSequence mMessage;
+
+ DismissKeyguardParam(@NonNull ActivityRecord activity,
+ @Nullable IKeyguardDismissCallback callback, @Nullable CharSequence message) {
+ mActivity = activity;
+ mCallback = callback;
+ mMessage = message;
+ }
+ }
+
+ static final class TopActivityOccludesKeyguardParam {
+ boolean mRet;
+ @NonNull final ActivityRecord mActivity;
+
+ TopActivityOccludesKeyguardParam(@NonNull ActivityRecord activity) {
+ mActivity = activity;
+ }
+ }
+
+ static final class DumpParam {
+ ArrayList<String> mRet = new ArrayList<>();
+ String mPrefix;
+
+ DumpParam(@NonNull String prefix) {
+ mPrefix = prefix;
+ }
+ }
+
+ KeyguardDisplayStateMachine(int displayId, @KeyguardState int initialState) {
+ super(initialState);
+ mDisplayId = displayId;
+ }
+
+ @Nullable
+ @Override
+ public Handler addStateHandler(int state, Handler handler) {
+ Handler prevHandler = super.addStateHandler(state, handler);
+ if (prevHandler != null) {
+ throw new IllegalStateException(
+ "Duplicate state handler registration: display=" + mDisplayId
+ + ", state=" + state);
+ }
+ return null;
+ }
+
+ @Override
+ public void transit(@KeyguardState int newState) {
+ if (DEBUG) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[ ");
+ for (Command cmd : getCommands()) {
+ sb.append(cmd);
+ sb.append(' ');
+ }
+ sb.append(" ]");
+ Slog.d(TAG, "State change: display=" + mDisplayId
+ + ", current=" + keyguardStateToString(getCurrentState())
+ + ", lastRequested=" + keyguardStateToString(getState())
+ + ", newState=" + keyguardStateToString(newState)
+ + ", command=" + sb
+ + ", stack=" + Debug.getCallers(30));
+ }
+ super.transit(newState);
+ }
+
+ @Override
+ public void enter(@KeyguardState int state) {
+ if (DEBUG) {
+ Slog.d(TAG, "enter: display=" + mDisplayId + ", state="
+ + keyguardStateToString(state));
+ }
+ super.enter(state);
+ }
+
+ @Override
+ public void exit(@KeyguardState int state) {
+ if (DEBUG) {
+ Slog.d(TAG, "exit: display=" + mDisplayId + ", state="
+ + keyguardStateToString(state));
+ }
+ super.exit(state);
+ }
+
+ void handleDismissKeyguardActivity() {
+ handle(EVENT_DISMISS_KEYGUARD_ACTIVITY, null /* param */);
+ }
+
+ void handleTurnScreenOnActivity() {
+ handle(EVENT_TURN_SCREEN_ON_ACTIVITY, null /* param */);
+ }
+
+ boolean handleDismissKeyguard(@NonNull ActivityRecord r,
+ @Nullable IKeyguardDismissCallback callback, @Nullable CharSequence message) {
+ DismissKeyguardParam param = new DismissKeyguardParam(r, callback, message);
+ handle(EVENT_DISMISS_KEYGUARD_API, param);
+ return param.mRet;
+ }
+
+ void handleShowWhenLockedActivity() {
+ handle(EVENT_SHOW_WHEN_LOCKED_ACTIVITY, null /* param */);
+ }
+
+ void handleLayoutChanges() {
+ handle(EVENT_LAYOUT_CHANGES, null /* param */);
+ }
+
+ boolean checkKeyguardVisibility(@NonNull ActivityRecord r) {
+ final CheckKeyguardVisibilityParam param = new CheckKeyguardVisibilityParam(r);
+ handle(EVENT_CHECK_KEYGUARD_VISIBILITY, param);
+ return param.mRet;
+ }
+
+ boolean topActivityOccludesKeyguard(@NonNull ActivityRecord r) {
+ final TopActivityOccludesKeyguardParam param = new TopActivityOccludesKeyguardParam(r);
+ handle(EVENT_TOP_ACTIVITY_OCCLUDES_KEYGUARD, param);
+ return param.mRet;
+ }
+
+ ArrayList<String> handleDump(String prefix) {
+ final DumpParam param = new DumpParam(prefix);
+ handle(EVENT_DUMP, param);
+ return param.mRet;
+ }
+ }
+
+ /**
+ * Helper class for implementing handler in type-safe way.
+ */
+ private abstract static class Handler implements StateMachine.Handler {
+ @Override
+ public final boolean handle(int event, @Nullable Object param) {
+ switch (event) {
+ case KeyguardDisplayStateMachine.EVENT_DISMISS_KEYGUARD_ACTIVITY:
+ return handleDismissKeyguardActivity();
+ case KeyguardDisplayStateMachine.EVENT_TURN_SCREEN_ON_ACTIVITY:
+ return handleTurnScreenOnActivity();
+ case KeyguardDisplayStateMachine.EVENT_DISMISS_KEYGUARD_API: {
+ final KeyguardDisplayStateMachine.DismissKeyguardParam typedParam =
+ (KeyguardDisplayStateMachine.DismissKeyguardParam) param;
+ Optional<Boolean> ret = handleDismissKeyguard(typedParam.mActivity,
+ typedParam.mCallback, typedParam.mMessage);
+ if (ret.isPresent()) {
+ typedParam.mRet = ret.get();
+ return true;
+ }
+ return false;
+ }
+ case KeyguardDisplayStateMachine.EVENT_SHOW_WHEN_LOCKED_ACTIVITY:
+ return handleShowWhenLockedActivity();
+ case KeyguardDisplayStateMachine.EVENT_CHECK_KEYGUARD_VISIBILITY: {
+ final KeyguardDisplayStateMachine.CheckKeyguardVisibilityParam typedParam =
+ (KeyguardDisplayStateMachine.CheckKeyguardVisibilityParam) param;
+ Optional<Boolean> ret = checkKeyguardVisibility(typedParam.mActivity);
+ if (ret.isPresent()) {
+ typedParam.mRet = ret.get();
+ return true;
+ }
+ return false;
+ }
+ case KeyguardDisplayStateMachine.EVENT_TOP_ACTIVITY_OCCLUDES_KEYGUARD: {
+ final KeyguardDisplayStateMachine.TopActivityOccludesKeyguardParam typedParam =
+ (KeyguardDisplayStateMachine.TopActivityOccludesKeyguardParam) param;
+ Optional<Boolean> ret = topActivityOccludesKeyguardParam(typedParam.mActivity);
+ if (ret.isPresent()) {
+ typedParam.mRet = ret.get();
+ return true;
+ }
+ return false;
+ }
+ case KeyguardDisplayStateMachine.EVENT_LAYOUT_CHANGES:
+ return handleLayoutChanges();
+ case KeyguardDisplayStateMachine.EVENT_DUMP:
+ final KeyguardDisplayStateMachine.DumpParam typedParam =
+ (KeyguardDisplayStateMachine.DumpParam) param;
+ String dumpInfo = handleDump(typedParam.mPrefix);
+ if (dumpInfo != null) {
+ typedParam.mRet.add(dumpInfo);
+ }
+ // keep collecting information for dump up to top status.
+ return false;
+ default:
+ Slog.e(TAG, "Handler.handle(): Unknown event(" + event + ")");
+ return false;
+ }
+ }
+
+ Optional<Boolean> checkKeyguardVisibility(@NonNull ActivityRecord activity) {
+ return Optional.empty();
+ }
+
+ Optional<Boolean> topActivityOccludesKeyguardParam(@NonNull ActivityRecord r) {
+ return Optional.empty();
+ }
+
+ /**
+ * Handle flags in the activity which request to dismiss the keyguard.
+ *
+ * @see ActivityOptions#setDismissKeyguard()
+ * @see WindowManager.LayoutParams#FLAG_DISMISS_KEYGUARD
+ */
+ boolean handleDismissKeyguardActivity() {
+ return false;
+ }
+
+ /**
+ * Handle flags in the activity which request to turn the screen on. This must be called
+ * after dismiss keyguard flag is handled.
+ */
+ boolean handleTurnScreenOnActivity() {
+ return false;
+ }
+ /**
+ * Handle flags in the activity which decides if the activity can be shown on top of the
+ * keyguard.
+ *
+ * @see android.app.Activity#setShowWhenLocked(boolean)
+ * @see android.app.Activity#setInheritShowWhenLocked(boolean)
+ */
+ boolean handleShowWhenLockedActivity() {
+ return false;
+ }
+
+ /**
+ * Request relayout if necessary.
+ */
+ boolean handleLayoutChanges() {
+ return false;
+ }
+
+ /**
+ * Called when the activity requests to dismiss the keyguard via KeyguardManager APIs.
+ *
+ * @param r The activity which requested to dismiss the keyguard.
+ * @return Present if the state handles, delegate to its parent state otherwise. When the
+ * value is present, the value is {@code true} if the keyguard dismiss request is
+ * processed, {@code false} otherwise.
+ */
+ Optional<Boolean> handleDismissKeyguard(@NonNull ActivityRecord r,
+ @Nullable IKeyguardDismissCallback callback, @Nullable CharSequence message) {
+ return Optional.empty();
+ }
+
+ @Nullable String handleDump(@NonNull String prefix) {
+ return null;
+ }
+ }
+
+ private static class DisplayState {
+ private final int mDisplayId;
+ @NonNull private final ServiceDelegate mServiceDelegate;
+ private final KeyguardDisplayStateMachine mStateMachine;
+
+ // TODO: Set watchdog timer to sync mLastNotifiedOccludedState == isIn(OCCLUDED)
+ private boolean mLastNotifiedOccludedState = false;
+
+ // Top activity which has a window with FLAG_DISMISS_KEYGUARD flag. Valid only when the
+ // current state is KEYGUARD_STATE_ON or one of its sub states.
+ @Nullable private ActivityRecord mDismissingKeyguardActivity;
+
+ // KeyguardController has requested to dismiss keyguard via IWindowManager#dismissKeyguard.
+ // Reset this to false again, once the KeyguardController status is updated.
+ private boolean mDismissalRequested = false;
+
+ DisplayState(int displayId, @NonNull ServiceDelegate serviceDelegate) {
+ mDisplayId = displayId;
+ mServiceDelegate = serviceDelegate;
+ mStateMachine = new KeyguardDisplayStateMachine(displayId, KEYGUARD_STATE_OFF);
+
+ mStateMachine.addStateHandler(KEYGUARD_STATE_ROOT, new Handler() {
+ @Override
+ Optional<Boolean> checkKeyguardVisibility(@NonNull ActivityRecord activity) {
+ return Optional.of(false);
+ }
+
+ @Override
+ Optional<Boolean> topActivityOccludesKeyguardParam(@NonNull ActivityRecord r) {
+ return Optional.of(false);
+ }
+ });
+
+ mStateMachine.addStateHandler(KEYGUARD_STATE_OFF, new Handler() {
+ @Override
+ public Optional<Boolean> checkKeyguardVisibility(@NonNull ActivityRecord r) {
+ return Optional.of(true);
+ }
+
+ @Override
+ Optional<Boolean> handleDismissKeyguard(
+ @NonNull ActivityRecord r, @Nullable IKeyguardDismissCallback callback,
+ @Nullable CharSequence message) {
+ // Keyguard is not shown, so we don't handle the request to dismiss the
+ // keyguard.
+ return Optional.of(false);
+ }
+ });
+
+ mStateMachine.addStateHandler(KEYGUARD_STATE_GOING_AWAY, new Handler() {
+ @Override
+ public void enter() {
+ mServiceDelegate.deferWindowLayout();
+ try {
+ mServiceDelegate.requestTransitionIfNeeded(mDisplayId,
+ TRANSIT_KEYGUARD_GOING_AWAY,
+ TRANSIT_FLAG_KEYGUARD_GOING_AWAY
+ | TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER);
+ // Some stack visibility might change (e.g. docked stack)
+ mServiceDelegate.releaseSleepToken(mDisplayId,
+ true /* resumeTopActivities */);
+ mServiceDelegate.executeAppTransition();
+ } finally {
+ mServiceDelegate.continueWindowLayout();
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
+ }
+ });
+
+ mStateMachine.addStateHandler(KEYGUARD_STATE_ON, new Handler() {
+ public boolean handleDismissKeyguardActivity() {
+ final ActivityRecord lastDismissingKeyguardActivity =
+ mDismissingKeyguardActivity;
+ final ActivityRecord top = mServiceDelegate.getTopNonFinishingActivity(
+ mDisplayId);
+ mDismissingKeyguardActivity =
+ (top != null && top.containsDismissKeyguardWindow()) ? top : null;
+ if (lastDismissingKeyguardActivity != mDismissingKeyguardActivity
+ && mDismissingKeyguardActivity != null
+ && mServiceDelegate.isKeyguardSecure()) {
+ // We only allow dismissing Keyguard via the flag when Keyguard is secure
+ // for legacy reasons, because that's how apps used to dismiss Keyguard in
+ // the secure case. In the insecure case, we actually show it on top of the
+ // lockscreen. See #canShowWhileOccluded.
+ mDismissalRequested = true;
+ mServiceDelegate.dismissKeyguard(null, null);
+ }
+ return true;
+ }
+
+ @Override
+ Optional<Boolean> handleDismissKeyguard(@NonNull ActivityRecord r,
+ @Nullable IKeyguardDismissCallback callback,
+ @Nullable CharSequence message) {
+ if (!r.visibleIgnoringKeyguard) {
+ return Optional.of(false);
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "Activity requesting to dismiss Keyguard: " + r);
+ }
+ // If the client has requested to dismiss the keyguard and the Activity has the
+ // flag to turn the screen on, wakeup the screen if it's the top Activity.
+ // Note that it's possible that the client requests to dismiss the keyguard
+ // before the activity adds a window. In this case the flag set on the window
+ // is not yet visible from ActivityRecord, so we need to check the flag again
+ // when the activity adds a window later. See #handleTurnScreenOnActivity().
+ if (r.getTurnScreenOnFlag() && r.isTopRunningActivity()) {
+ mServiceDelegate.wakeUp("ON/handleDismissKeyguard");
+ r.setCurrentLaunchCanTurnScreenOn(false);
+ }
+ mDismissalRequested = true;
+ mServiceDelegate.dismissKeyguard(callback, message);
+ return Optional.of(true);
+ }
+
+ @Override
+ public void enter() {
+ // Update the task snapshot if the screen will not be turned off. To make sure
+ // that the unlocking animation can animate consistent content.
+ mServiceDelegate.snapshotForSleeping(mDisplayId);
+ }
+
+ @Nullable
+ @Override
+ String handleDump(@NonNull String prefix) {
+ StringBuffer sb = new StringBuffer();
+ sb.append(prefix)
+ .append(" mDismissingKeyguardActivity=")
+ .append(mDismissingKeyguardActivity)
+ .append("\n");
+ return sb.toString();
+ }
+ });
+
+ mStateMachine.addStateHandler(KEYGUARD_STATE_OCCLUDED, new Handler() {
+ ActivityRecord mTopOccludesActivity;
+
+ @Override
+ Optional<Boolean> checkKeyguardVisibility(@NonNull ActivityRecord activity) {
+ return Optional.of(mServiceDelegate.canOcclude(activity));
+ }
+
+ @Override
+ public boolean handleShowWhenLockedActivity() {
+ final ActivityRecord top = mServiceDelegate.getTopNonFinishingActivity(
+ mDisplayId);
+ final ActivityRecord topOccludesActivity = mServiceDelegate.canOcclude(top)
+ ? top : null;
+ if (mTopOccludesActivity == topOccludesActivity) {
+ return true;
+ }
+ // Launch SHOW_WHEN_LOCKED or INHERIT_SHOW_WHEN_LOCKED activity on top of an
+ // occluding activity.
+ mTopOccludesActivity = topOccludesActivity;
+ if (mServiceDelegate.isTopActivityDreaming(mDisplayId)) {
+ // Dream activity is launched on top of the previous SHOW_WHEN_LOCKED
+ // activity.
+ setKeyguardState(KEYGUARD_STATE_DREAMING);
+ } else if (topOccludesActivity == null) {
+ // SHOW_WHEN_LOCKED activity finishes.
+ setKeyguardState(KEYGUARD_STATE_LOCKSCREEN_SHOWN);
+ }
+ return true;
+ }
+
+ @Override
+ boolean handleLayoutChanges() {
+ // The occluding activity may be translucent or not fill screen. Then let
+ // wallpaper to check whether it should set itself as target to avoid blank
+ // background.
+ if (!mTopOccludesActivity.fillsParent()) {
+ mServiceDelegate.requestLayoutRedoWallpaper(mDisplayId);
+ }
+ return true;
+ }
+
+ @Override
+ Optional<Boolean> topActivityOccludesKeyguardParam(@NonNull ActivityRecord r) {
+ return Optional.of(mTopOccludesActivity == r);
+ }
+
+ @Override
+ public void enter() {
+ mTopOccludesActivity = mServiceDelegate.getTopNonFinishingActivity(mDisplayId);
+ if (!mServiceDelegate.canOcclude(mTopOccludesActivity)) {
+ Slog.e(TAG, "enter(OCCLUDE): no occluding activity");
+ setKeyguardState(KEYGUARD_STATE_LOCKSCREEN_SHOWN);
+ return;
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "handleOccludedChanged: display=" + mDisplayId
+ + ", topActivity=" + mTopOccludesActivity);
+ }
+ // Collect the participates for shell transition, so that transition won't
+ // happen too early since the transition was set ready.
+ mServiceDelegate.collect(mTopOccludesActivity);
+ // TODO(b/113840485): Handle app transition for individual display, and apply
+ // occluded state change to secondary displays. For now, only default display
+ // fully supports occluded change. Other displays only updates keyguard sleep
+ // token on that display.
+ if (mDisplayId != DEFAULT_DISPLAY) {
+ mServiceDelegate.releaseSleepToken(mDisplayId,
+ false /* resumeTopActivities */);
+ return;
+ }
+
+ if (mTopOccludesActivity.getTurnScreenOnFlag()
+ && mTopOccludesActivity.currentLaunchCanTurnScreenOn()
+ && !mServiceDelegate.isDeviceInteractive()) {
+ mServiceDelegate.wakeUp("OCCLUDE/enter");
+ mTopOccludesActivity.setCurrentLaunchCanTurnScreenOn(false);
+ }
+
+ mServiceDelegate.notifyKeyguardOccludeChanged(true /* occluded */);
+ mServiceDelegate.deferWindowLayout();
+ try {
+ mServiceDelegate.requestTransitionIfNeeded(mDisplayId,
+ TRANSIT_KEYGUARD_OCCLUDE, 0 /* flags */);
+ mServiceDelegate.releaseSleepToken(mDisplayId,
+ false /* resumeTopActivities */);
+ mServiceDelegate.executeAppTransition();
+ } finally {
+ mServiceDelegate.continueWindowLayout();
+ }
+ // Dismiss freeform windowing mode
+ final Task currentTaskControllingOcclusion = mTopOccludesActivity.getRootTask();
+ if (currentTaskControllingOcclusion != null
+ && currentTaskControllingOcclusion.inFreeformWindowingMode()) {
+ currentTaskControllingOcclusion.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ }
+ }
+
+ @Override
+ public void exit() {
+ mTopOccludesActivity = null;
+ if (DEBUG) {
+ Slog.d(TAG, "handleOccludedChanged: topActivity=" + null);
+ }
+ // TODO(b/113840485): Handle app transition for individual display, and apply
+ // occluded state change to secondary displays.
+ // For now, only default display fully supports occluded change. Other displays
+ // only updates keyguard sleep token on that display.
+ if (mDisplayId != DEFAULT_DISPLAY) {
+ mServiceDelegate.acquireSleepToken(
+ mDisplayId, false /* ensureActivitiesVisible */);
+ return;
+ }
+
+ mServiceDelegate.notifyKeyguardOccludeChanged(false /* occluded */);
+ mServiceDelegate.deferWindowLayout();
+ try {
+ mServiceDelegate.requestTransitionIfNeeded(mDisplayId,
+ TRANSIT_KEYGUARD_UNOCCLUDE, 0 /* flags */);
+ mServiceDelegate.acquireSleepToken(
+ mDisplayId, false /* ensureActivitiesVisible */);
+ mServiceDelegate.executeAppTransition();
+ } finally {
+ mServiceDelegate.continueWindowLayout();
+ }
+ }
+
+ @Nullable
+ @Override
+ String handleDump(@NonNull String prefix) {
+ StringBuffer sb = new StringBuffer();
+ sb.append(prefix)
+ .append(" mTopOccludesActivity=")
+ .append(mTopOccludesActivity)
+ .append("\n");
+ return sb.toString();
+ }
+ });
+
+ mStateMachine.addStateHandler(KEYGUARD_STATE_KEYGUARD_TOP, new Handler() {
+ @Override
+ public boolean handleDismissKeyguardActivity() {
+ final ActivityRecord top = mServiceDelegate.getTopNonFinishingActivity(
+ mDisplayId);
+ if (top != null && top.mDismissKeyguard) {
+ // Top activity has been launched with ActivityOptions#setDismissKeyguard.
+ // Authentication has already been passed, so we can turn off the keyguard
+ // immediately.
+ top.mDismissKeyguard = false;
+ setKeyguardState(KEYGUARD_STATE_GOING_AWAY);
+ // Collect the participates for shell transition, so that transition won't
+ // happen too early since the transition was set ready.
+ mServiceDelegate.collect(top);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void enter() {
+ mServiceDelegate.acquireSleepToken(mDisplayId,
+ true /* ensureActivitiesVisible */);
+ InputMethodManagerInternal.get().updateImeWindowStatus(
+ false /* disableImeIcon */);
+ mServiceDelegate.setWakeTransitionReady();
+ }
+
+ @Override
+ public void exit() {
+ // Sleep token is released in enter() action in other states, since we need
+ // to call requestTransition() before updating visibility of the activities.
+ }
+ });
+
+ mStateMachine.addStateHandler(KEYGUARD_STATE_LOCKSCREEN_SHOWN, new Handler() {
+ @Override
+ public Optional<Boolean> checkKeyguardVisibility(@NonNull ActivityRecord r) {
+ // If lock screen is showing, nothing is visible, except if we are able to
+ // dismiss Keyguard right away. This isn't allowed if r is already the
+ // dismissing activity, in which case we don't allow it to repeatedly
+ // dismiss Keyguard.
+ return Optional.of(r.containsDismissKeyguardWindow()
+ && mServiceDelegate.canDismissKeyguard()
+ && (mDismissalRequested
+ || (r.canShowWhenLocked() && mDismissingKeyguardActivity != r)));
+ }
+
+ @Override
+ public boolean handleShowWhenLockedActivity() {
+ final ActivityRecord top = mServiceDelegate.getTopNonFinishingActivity(
+ mDisplayId);
+ final ActivityRecord topOccludesActivity = mServiceDelegate.canOcclude(top)
+ ? top : null;
+ if (topOccludesActivity != null) {
+ setKeyguardState(mServiceDelegate.isTopActivityDreaming(mDisplayId)
+ ? KEYGUARD_STATE_DREAMING : KEYGUARD_STATE_OCCLUDED);
+ }
+ return true;
+ }
+ });
+
+ mStateMachine.addStateHandler(KEYGUARD_STATE_AOD_SHOWN, new Handler() {
+ // Top activity which has FLAG_TURN_SCREEN_ON flag.
+ @Nullable private ActivityRecord mTopTurnScreenOnActivity;
+
+ @Override
+ public Optional<Boolean> checkKeyguardVisibility(@NonNull ActivityRecord r) {
+ return Optional.of(false);
+ }
+
+ @Override
+ boolean handleTurnScreenOnActivity() {
+ final ActivityRecord lastTopTurnScreenOnActivity = mTopTurnScreenOnActivity;
+ final ActivityRecord top = mServiceDelegate.getTopNonFinishingActivity(
+ mDisplayId);
+ mTopTurnScreenOnActivity = (top != null && top.getTurnScreenOnFlag()
+ && top.currentLaunchCanTurnScreenOn()) ? top : null;
+ if (mTopTurnScreenOnActivity != lastTopTurnScreenOnActivity
+ && mTopTurnScreenOnActivity != null
+ && !mServiceDelegate.isDeviceInteractive()
+ && mDismissalRequested) {
+ mServiceDelegate.wakeUp("AOD_SHOWN/handleTurnScreenOnActivity");
+ mTopTurnScreenOnActivity.setCurrentLaunchCanTurnScreenOn(false);
+ }
+ return true;
+ }
+
+ @Override
+ public void enter() {
+ if (mLastNotifiedOccludedState) {
+ if (mDisplayId == DEFAULT_DISPLAY) {
+ mServiceDelegate.forceSyncOccludedStatus(false);
+ }
+ commitOccludedStatus(false);
+ }
+ }
+
+ @Override
+ public void exit() {
+ mServiceDelegate.updateDeferTransitionForAod(false /* waiting */);
+ }
+
+ @Nullable
+ @Override
+ String handleDump(@NonNull String prefix) {
+ StringBuffer sb = new StringBuffer();
+ sb.append(prefix)
+ .append(" mTopTurnScreenOnActivity=")
+ .append(mTopTurnScreenOnActivity)
+ .append("\n");
+ return sb.toString();
+ }
+ });
+ }
+
+ void onRemoved() {
+ mServiceDelegate.releaseSleepToken(mDisplayId, false /* resumeTopActivities */);
+ }
+
+ void updateVisibility() {
+ mStateMachine.handleDismissKeyguardActivity();
+ mStateMachine.handleTurnScreenOnActivity();
+ mStateMachine.handleShowWhenLockedActivity();
+ mStateMachine.handleLayoutChanges();
+ }
+
+ boolean dismissKeyguard(@NonNull ActivityRecord r,
+ @Nullable IKeyguardDismissCallback callback,
+ @Nullable CharSequence message) {
+ return mStateMachine.handleDismissKeyguard(r, callback, message);
+ }
+
+ void commitOccludedStatus(boolean occluded) {
+ mLastNotifiedOccludedState = occluded;
+ }
+
+ void setKeyguardState(@KeyguardState int newState) {
+ mDismissalRequested = false;
+ mStateMachine.transit(newState);
+ }
+
+ boolean isKeyguardTop() {
+ return mStateMachine.isIn(KEYGUARD_STATE_KEYGUARD_TOP);
+ }
+
+ boolean isIn(@KeyguardState int category) {
+ return mStateMachine.isIn(category);
+ }
+
+ boolean topActivityOccludesKeyguard(@NonNull ActivityRecord r) {
+ return mStateMachine.topActivityOccludesKeyguard(r);
+ }
+
+ boolean checkKeyguardVisibility(@NonNull ActivityRecord r) {
+ return mStateMachine.checkKeyguardVisibility(r);
+ }
+
+ void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(KeyguardPerDisplayProto.DISPLAY_ID, mDisplayId);
+ proto.write(KeyguardPerDisplayProto.KEYGUARD_SHOWING,
+ isIn(KEYGUARD_STATE_LOCKSCREEN_SHOWN));
+ proto.write(KeyguardPerDisplayProto.AOD_SHOWING, isIn(KEYGUARD_STATE_AOD_SHOWN));
+ proto.write(KeyguardPerDisplayProto.KEYGUARD_OCCLUDED, isIn(KEYGUARD_STATE_OCCLUDED));
+ proto.end(token);
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ StringBuffer sb = new StringBuffer();
+ sb.append(prefix)
+ .append("* display=")
+ .append(mDisplayId)
+ .append("\n");
+ sb.append(prefix)
+ .append(" state=")
+ .append(keyguardStateToString(mStateMachine.getState()))
+ .append("\n");
+ sb.append(prefix)
+ .append(" mLastNotifiedOccludedState=")
+ .append(mLastNotifiedOccludedState)
+ .append("\n");
+ sb.append(prefix)
+ .append(" mDismissalRequested=")
+ .append(mDismissalRequested)
+ .append("\n");
+ pw.print(sb.toString());
+
+ ArrayList<String> dumpInfo = mStateMachine.handleDump(prefix);
+ for (int i = dumpInfo.size() - 1; i >= 0; --i) {
+ pw.print(dumpInfo.get(i));
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 5e116ba..80965a7 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -730,7 +730,7 @@
void dispatchLegacyAppTransitionStarting(TransitionInfo info, long statusBarTransitionDelay) {
for (int i = 0; i < mLegacyListeners.size(); ++i) {
- // TODO(shell-transitions): handle (un)occlude transition.
+ mLegacyListeners.get(i).onAppTransitionStartingLocked(info);
mLegacyListeners.get(i).onAppTransitionStartingLocked(
SystemClock.uptimeMillis() + statusBarTransitionDelay,
AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 1282acb..ad6bd3c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -44,6 +44,7 @@
import android.view.WindowInfo;
import android.view.WindowManager.DisplayImePolicy;
import android.view.inputmethod.ImeTracker;
+import android.window.TransitionInfo;
import com.android.internal.policy.KeyInterceptionInfo;
import com.android.server.input.InputManagerService;
@@ -212,7 +213,7 @@
* Abstract class to be notified about {@link com.android.server.wm.AppTransition} events. Held
* as an abstract class so a listener only needs to implement the methods of its interest.
*/
- public static abstract class AppTransitionListener {
+ public abstract static class AppTransitionListener {
/**
* Called when an app transition is being setup and about to be executed.
@@ -251,6 +252,20 @@
}
/**
+ * Called when an app transition gets started when WM shell is enabled.
+ *
+ * @param info Information about what is changing during a transition.
+ *
+ * @return Return any bit set of {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_LAYOUT},
+ * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_CONFIG},
+ * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_WALLPAPER},
+ * or {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_ANIM}.
+ */
+ public int onAppTransitionStartingLocked(TransitionInfo info) {
+ return 0;
+ }
+
+ /**
* Called when an app transition is finished running.
*
* @param token the token for app whose transition has finished
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a596eed..f6cb068 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3132,7 +3132,7 @@
@Override
public void notifyKeyguardTrustedChanged() {
synchronized (mGlobalLock) {
- if (mAtmService.mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) {
+ if (mAtmService.mKeyguardController.isLocksScreenShowing(DEFAULT_DISPLAY)) {
mRoot.ensureActivitiesVisible(null, 0, false /* preserveWindows */);
}
}
diff --git a/services/core/jni/com_android_server_display_DisplayControl.cpp b/services/core/jni/com_android_server_display_DisplayControl.cpp
index 1859333..ec42324 100644
--- a/services/core/jni/com_android_server_display_DisplayControl.cpp
+++ b/services/core/jni/com_android_server_display_DisplayControl.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <android/gui/IHdrConversionConstants.h>
#include <android_util_Binder.h>
#include <gui/SurfaceComposerClient.h>
#include <jni.h>
@@ -55,6 +56,58 @@
}
}
+static void nativeSetHdrConversionMode(JNIEnv* env, jclass clazz, jint hdrConversionMode,
+ jint preferredHdrOutputType, jintArray autoHdrOutputTypes,
+ jint autoHdrOutputTypesLength) {
+ gui::HdrConversionStrategy hdrConversionStrategy;
+ switch (hdrConversionMode) {
+ case gui::IHdrConversionConstants::HdrConversionModePassthrough: {
+ hdrConversionStrategy.set<gui::HdrConversionStrategy::Tag::passthrough>(true);
+ break;
+ }
+ case gui::IHdrConversionConstants::HdrConversionModeAuto: {
+ jint* autoHdrOutputTypesArray = env->GetIntArrayElements(autoHdrOutputTypes, 0);
+ std::vector<int> autoHdrOutputTypesVector(autoHdrOutputTypesLength);
+ for (int i = 0; i < autoHdrOutputTypesLength; i++) {
+ autoHdrOutputTypesVector[i] = autoHdrOutputTypesArray[i];
+ }
+ hdrConversionStrategy.set<gui::HdrConversionStrategy::Tag::autoAllowedHdrTypes>(
+ autoHdrOutputTypesVector);
+ break;
+ }
+ case gui::IHdrConversionConstants::HdrConversionModeForce: {
+ hdrConversionStrategy.set<gui::HdrConversionStrategy::Tag::forceHdrConversion>(
+ preferredHdrOutputType);
+ break;
+ }
+ }
+
+ SurfaceComposerClient::setHdrConversionStrategy(hdrConversionStrategy);
+}
+
+static jintArray nativeGetSupportedHdrOutputTypes(JNIEnv* env, jclass clazz) {
+ std::vector<gui::HdrConversionCapability> hdrConversionCapabilities;
+ SurfaceComposerClient::getHdrConversionCapabilities(&hdrConversionCapabilities);
+
+ // Extract unique HDR output types.
+ std::set<int> hdrOutputTypes;
+ for (const auto& hdrConversionCapability : hdrConversionCapabilities) {
+ hdrOutputTypes.insert(hdrConversionCapability.outputType);
+ }
+ jintArray array = env->NewIntArray(hdrOutputTypes.size());
+ if (array == nullptr) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", nullptr);
+ return nullptr;
+ }
+ jint* arrayValues = env->GetIntArrayElements(array, 0);
+ int index = 0;
+ for (auto hdrOutputType : hdrOutputTypes) {
+ arrayValues[index++] = static_cast<jint>(hdrOutputType);
+ }
+ env->ReleaseIntArrayElements(array, arrayValues, 0);
+ return array;
+}
+
static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) {
const auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
ScopedLongArrayRW values(env, env->NewLongArray(displayIds.size()));
@@ -91,6 +144,10 @@
(void*)nativeGetPhysicalDisplayIds },
{"nativeGetPhysicalDisplayToken", "(J)Landroid/os/IBinder;",
(void*)nativeGetPhysicalDisplayToken },
+ {"nativeSetHdrConversionMode", "(II[II)V",
+ (void*)nativeSetHdrConversionMode },
+ {"nativeGetSupportedHdrOutputTypes", "()[I",
+ (void*)nativeGetSupportedHdrOutputTypes },
// clang-format on
};
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 7f85347..a27eb7f 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -202,12 +202,15 @@
</xs:simpleType>
<xs:complexType name="thermalThrottling">
- <xs:complexType>
+ <xs:sequence>
<xs:element type="brightnessThrottlingMap" name="brightnessThrottlingMap">
<xs:annotation name="nonnull"/>
<xs:annotation name="final"/>
</xs:element>
- </xs:complexType>
+ <xs:element type="brightnessThrottlingMap" name="concurrentDisplaysBrightnessThrottlingMap">
+ <xs:annotation name="final"/>
+ </xs:element>
+ </xs:sequence>
</xs:complexType>
<xs:complexType name="brightnessThrottlingMap">
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index cb08179..6c66d0d 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -237,7 +237,9 @@
public class ThermalThrottling {
ctor public ThermalThrottling();
method @NonNull public final com.android.server.display.config.BrightnessThrottlingMap getBrightnessThrottlingMap();
+ method public final com.android.server.display.config.BrightnessThrottlingMap getConcurrentDisplaysBrightnessThrottlingMap();
method public final void setBrightnessThrottlingMap(@NonNull com.android.server.display.config.BrightnessThrottlingMap);
+ method public final void setConcurrentDisplaysBrightnessThrottlingMap(com.android.server.display.config.BrightnessThrottlingMap);
}
public class ThresholdPoint {
diff --git a/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java b/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
index b7c5fc2..865cba8 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialDescriptionRegistry.java
@@ -17,11 +17,8 @@
package com.android.server.credentials;
import android.credentials.CredentialDescription;
-import android.credentials.IRegisterCredentialDescriptionCallback;
-import android.credentials.IUnregisterCredentialDescriptionCallback;
import android.credentials.RegisterCredentialDescriptionRequest;
import android.credentials.UnregisterCredentialDescriptionRequest;
-import android.os.RemoteException;
import android.util.SparseArray;
import java.util.HashMap;
@@ -61,8 +58,7 @@
/** Handle the given {@link RegisterCredentialDescriptionRequest} by creating
* the appropriate package name mapping. */
public void executeRegisterRequest(RegisterCredentialDescriptionRequest request,
- String callingPackageName,
- IRegisterCredentialDescriptionCallback callback) {
+ String callingPackageName) {
if (!mCredentialDescriptions.containsKey(callingPackageName)
&& mCredentialDescriptions.size() <= MAX_ALLOWED_CREDENTIAL_DESCRIPTIONS) {
@@ -71,30 +67,17 @@
mCredentialDescriptions.get(callingPackageName)
.addAll(request.getCredentialDescriptions());
-
- try {
- callback.onResponse();
- } catch (RemoteException e) {
- e.printStackTrace();
- }
}
/** Handle the given {@link UnregisterCredentialDescriptionRequest} by creating
* the appropriate package name mapping. */
public void executeUnregisterRequest(
UnregisterCredentialDescriptionRequest request,
- String callingPackageName,
- IUnregisterCredentialDescriptionCallback callback) {
+ String callingPackageName) {
if (mCredentialDescriptions.containsKey(callingPackageName)) {
mCredentialDescriptions.get(callingPackageName)
- .remove(request.getCredentialDescription());
- }
-
- try {
- callback.onResponse();
- } catch (RemoteException e) {
- e.printStackTrace();
+ .removeAll(request.getCredentialDescriptions());
}
}
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 620b81b..bb26fa9 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -28,6 +28,7 @@
import android.credentials.ClearCredentialStateRequest;
import android.credentials.CreateCredentialException;
import android.credentials.CreateCredentialRequest;
+import android.credentials.CredentialDescription;
import android.credentials.GetCredentialException;
import android.credentials.GetCredentialOption;
import android.credentials.GetCredentialRequest;
@@ -36,9 +37,7 @@
import android.credentials.ICredentialManager;
import android.credentials.IGetCredentialCallback;
import android.credentials.IListEnabledProvidersCallback;
-import android.credentials.IRegisterCredentialDescriptionCallback;
import android.credentials.ISetEnabledProvidersCallback;
-import android.credentials.IUnregisterCredentialDescriptionCallback;
import android.credentials.ListEnabledProvidersResponse;
import android.credentials.RegisterCredentialDescriptionRequest;
import android.credentials.UnregisterCredentialDescriptionRequest;
@@ -274,8 +273,8 @@
request.getGetCredentialOptions().stream().map(
getCredentialOption -> getCredentialOption
.getCredentialRetrievalData()
- .getString(RegisterCredentialDescriptionRequest
- .FLATTENED_REQUEST_STRING_KEY))
+ .getString(GetCredentialOption
+ .FLATTENED_REQUEST))
.collect(Collectors.toSet());
// All requested credential descriptions based on the given request.
@@ -543,12 +542,10 @@
}
@Override
- public ICancellationSignal registerCredentialDescription(
- RegisterCredentialDescriptionRequest request,
- IRegisterCredentialDescriptionCallback callback, String callingPackage) {
+ public void registerCredentialDescription(
+ RegisterCredentialDescriptionRequest request, String callingPackage)
+ throws IllegalArgumentException , NonCredentialProviderCallerException {
Log.i(TAG, "registerCredentialDescription");
- ICancellationSignal cancelTransport = CancellationSignal.createTransport();
-
List<CredentialProviderInfo> services =
CredentialProviderInfo.getAvailableServices(mContext,
@@ -557,31 +554,37 @@
List<String> providers = services.stream()
.map(credentialProviderInfo
-> credentialProviderInfo.getServiceInfo().packageName).toList();
+
if (!providers.contains(callingPackage)) {
- try {
- callback.onError("UNKNOWN",
- "Not an existing provider.");
- } catch (RemoteException e) {
- Log.i(
- TAG,
- "Issue invoking onError on IRegisterCredentialDescriptionCallback "
- + "callback: "
- + e.getMessage());
- }
+ throw new NonCredentialProviderCallerException(callingPackage);
+ }
+
+ List<CredentialProviderInfo> matchingService = services.stream().filter(
+ credentialProviderInfo ->
+ credentialProviderInfo.getServiceInfo()
+ .packageName.equals(callingPackage)).toList();
+
+ CredentialProviderInfo credentialProviderInfo = matchingService.get(0);
+
+ Set<String> supportedTypes = request.getCredentialDescriptions()
+ .stream().map(CredentialDescription::getType).filter(
+ credentialProviderInfo::hasCapability).collect(Collectors.toSet());
+
+ if (supportedTypes.size() != request.getCredentialDescriptions().size()) {
+ throw new IllegalArgumentException("CredentialProvider does not support one or more"
+ + "of the registered types. Check your XML entry.");
}
CredentialDescriptionRegistry session = CredentialDescriptionRegistry
.forUser(UserHandle.getCallingUserId());
- session.executeRegisterRequest(request, callingPackage, callback);
-
- return cancelTransport;
+ session.executeRegisterRequest(request, callingPackage);
}
@Override
- public ICancellationSignal unRegisterCredentialDescription(
- UnregisterCredentialDescriptionRequest request,
- IUnregisterCredentialDescriptionCallback callback, String callingPackage) {
+ public void unregisterCredentialDescription(
+ UnregisterCredentialDescriptionRequest request, String callingPackage)
+ throws IllegalArgumentException {
Log.i(TAG, "registerCredentialDescription");
ICancellationSignal cancelTransport = CancellationSignal.createTransport();
@@ -594,24 +597,13 @@
-> credentialProviderInfo.getServiceInfo().packageName).toList();
if (!providers.contains(callingPackage)) {
- try {
- callback.onError("UNKNOWN",
- "Not an existing provider.");
- } catch (RemoteException e) {
- Log.i(
- TAG,
- "Issue invoking onError on IRegisterCredentialDescriptionCallback "
- + "callback: "
- + e.getMessage());
- }
+ throw new NonCredentialProviderCallerException(callingPackage);
}
CredentialDescriptionRegistry session = CredentialDescriptionRegistry
.forUser(UserHandle.getCallingUserId());
- session.executeUnregisterRequest(request, callingPackage, callback);
-
- return cancelTransport;
+ session.executeUnregisterRequest(request, callingPackage);
}
}
}
diff --git a/services/credentials/java/com/android/server/credentials/NonCredentialProviderCallerException.java b/services/credentials/java/com/android/server/credentials/NonCredentialProviderCallerException.java
new file mode 100644
index 0000000..383d0aa
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/NonCredentialProviderCallerException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.credentials;
+
+/**
+ * Thrown when the calling app is not a Credential Provider.
+ */
+public class NonCredentialProviderCallerException extends RuntimeException {
+
+ private static final String MESSAGE = " is not an existing Credential Provider.";
+
+ public NonCredentialProviderCallerException(String caller) {
+ super(caller + MESSAGE);
+ }
+}
diff --git a/services/robotests/src/com/android/server/location/gnss/NtpTimeHelperTest.java b/services/robotests/src/com/android/server/location/gnss/NtpNetworkTimeHelperTest.java
similarity index 77%
rename from services/robotests/src/com/android/server/location/gnss/NtpTimeHelperTest.java
rename to services/robotests/src/com/android/server/location/gnss/NtpNetworkTimeHelperTest.java
index e5a1cfe..4949091 100644
--- a/services/robotests/src/com/android/server/location/gnss/NtpTimeHelperTest.java
+++ b/services/robotests/src/com/android/server/location/gnss/NtpNetworkTimeHelperTest.java
@@ -26,7 +26,7 @@
import android.platform.test.annotations.Presubmit;
import android.util.NtpTrustedTime;
-import com.android.server.location.gnss.NtpTimeHelper.InjectNtpTimeCallback;
+import com.android.server.location.gnss.NetworkTimeHelper.InjectTimeCallback;
import org.junit.Before;
import org.junit.Test;
@@ -41,16 +41,16 @@
import java.util.concurrent.TimeUnit;
/**
- * Unit tests for {@link NtpTimeHelper}.
+ * Unit tests for {@link NtpNetworkTimeHelper}.
*/
@RunWith(RobolectricTestRunner.class)
@Presubmit
-public class NtpTimeHelperTest {
+public class NtpNetworkTimeHelperTest {
private static final long MOCK_NTP_TIME = 1519930775453L;
@Mock
private NtpTrustedTime mMockNtpTrustedTime;
- private NtpTimeHelper mNtpTimeHelper;
+ private NtpNetworkTimeHelper mNtpNetworkTimeHelper;
private CountDownLatch mCountDownLatch;
/**
@@ -60,12 +60,12 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mCountDownLatch = new CountDownLatch(1);
- InjectNtpTimeCallback callback =
+ InjectTimeCallback callback =
(time, timeReference, uncertainty) -> {
assertThat(time).isEqualTo(MOCK_NTP_TIME);
mCountDownLatch.countDown();
};
- mNtpTimeHelper = new NtpTimeHelper(RuntimeEnvironment.application,
+ mNtpNetworkTimeHelper = new NtpNetworkTimeHelper(RuntimeEnvironment.application,
Looper.myLooper(),
callback, mMockNtpTrustedTime);
}
@@ -74,13 +74,13 @@
* Verify that cached time is returned if cached age is low.
*/
@Test
- public void handleInjectNtpTime_cachedAgeLow_injectTime() throws InterruptedException {
+ public void demandUtcTimeInjection_cachedAgeLow_injectTime() throws InterruptedException {
NtpTrustedTime.TimeResult result = mock(NtpTrustedTime.TimeResult.class);
- doReturn(NtpTimeHelper.NTP_INTERVAL - 1).when(result).getAgeMillis();
+ doReturn(NtpNetworkTimeHelper.NTP_INTERVAL - 1).when(result).getAgeMillis();
doReturn(MOCK_NTP_TIME).when(result).getTimeMillis();
doReturn(result).when(mMockNtpTrustedTime).getCachedTimeResult();
- mNtpTimeHelper.retrieveAndInjectNtpTime();
+ mNtpNetworkTimeHelper.demandUtcTimeInjection();
waitForTasksToBePostedOnHandlerAndRunThem();
assertThat(mCountDownLatch.await(2, TimeUnit.SECONDS)).isTrue();
@@ -90,14 +90,14 @@
* Verify that failed inject time and delayed inject time are handled properly.
*/
@Test
- public void handleInjectNtpTime_injectTimeFailed_injectTimeDelayed()
+ public void demandUtcTimeInjection_injectTimeFailed_injectTimeDelayed()
throws InterruptedException {
NtpTrustedTime.TimeResult result1 = mock(NtpTrustedTime.TimeResult.class);
- doReturn(NtpTimeHelper.NTP_INTERVAL + 1).when(result1).getAgeMillis();
+ doReturn(NtpNetworkTimeHelper.NTP_INTERVAL + 1).when(result1).getAgeMillis();
doReturn(result1).when(mMockNtpTrustedTime).getCachedTimeResult();
doReturn(false).when(mMockNtpTrustedTime).forceRefresh();
- mNtpTimeHelper.retrieveAndInjectNtpTime();
+ mNtpNetworkTimeHelper.demandUtcTimeInjection();
waitForTasksToBePostedOnHandlerAndRunThem();
assertThat(mCountDownLatch.await(2, TimeUnit.SECONDS)).isFalse();
@@ -106,15 +106,15 @@
doReturn(1L).when(result2).getAgeMillis();
doReturn(MOCK_NTP_TIME).when(result2).getTimeMillis();
doReturn(result2).when(mMockNtpTrustedTime).getCachedTimeResult();
- SystemClock.sleep(NtpTimeHelper.RETRY_INTERVAL);
+ SystemClock.sleep(NtpNetworkTimeHelper.RETRY_INTERVAL);
waitForTasksToBePostedOnHandlerAndRunThem();
assertThat(mCountDownLatch.await(2, TimeUnit.SECONDS)).isTrue();
}
/**
- * Since a thread is created in {@link NtpTimeHelper#retrieveAndInjectNtpTime} and the task to
- * be verified is posted in the thread, we have to wait for the task to be posted and then it
+ * Since a thread is created in {@link NtpNetworkTimeHelper#demandUtcTimeInjection} and the task
+ * to be verified is posted in the thread, we have to wait for the task to be posted and then it
* can be run.
*/
private void waitForTasksToBePostedOnHandlerAndRunThem() throws InterruptedException {
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index ced2a4b..3a7b9a4 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -62,6 +62,7 @@
"junit-params",
"ActivityContext",
"coretests-aidl",
+ "securebox",
],
libs: [
diff --git a/services/tests/servicestests/res/xml/usertypes_test_profile.xml b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
index 908e717..26d681b 100644
--- a/services/tests/servicestests/res/xml/usertypes_test_profile.xml
+++ b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
@@ -38,6 +38,9 @@
crossProfileIntentResolutionStrategy='0'
mediaSharedWithParent='true'
credentialShareableWithParent='false'
+ showInSettings='23'
+ inheritDevicePolicy='450'
+ deleteAppWithParent='false'
/>
</profile-type>
<profile-type name='custom.test.1' max-allowed-per-parent='14' />
diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallServiceTest.java
new file mode 100644
index 0000000..a488ab4
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallServiceTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.companion.datatransfer.contextsync;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Mockito.when;
+
+import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+@Presubmit
+@RunWith(AndroidTestingRunner.class)
+public class CallMetadataSyncInCallServiceTest {
+
+ private CallMetadataSyncInCallService mSyncInCallService;
+ @Mock
+ private CrossDeviceCall mMockCrossDeviceCall;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mSyncInCallService = new CallMetadataSyncInCallService();
+ }
+
+ @Test
+ public void getCallForId_invalid() {
+ when(mMockCrossDeviceCall.getId()).thenReturn(-1L);
+ final CrossDeviceCall call = mSyncInCallService.getCallForId(-1L,
+ List.of(mMockCrossDeviceCall));
+ assertWithMessage("Unexpectedly found a match for call id").that(call).isNull();
+ }
+
+ @Test
+ public void getCallForId_noMatch() {
+ when(mMockCrossDeviceCall.getId()).thenReturn(5L);
+ final CrossDeviceCall call = mSyncInCallService.getCallForId(1L,
+ List.of(mMockCrossDeviceCall));
+ assertWithMessage("Unexpectedly found a match for call id").that(call).isNull();
+ }
+
+ @Test
+ public void getCallForId_hasMatch() {
+ when(mMockCrossDeviceCall.getId()).thenReturn(5L);
+ final CrossDeviceCall call = mSyncInCallService.getCallForId(5L,
+ List.of(mMockCrossDeviceCall));
+ assertWithMessage("Unexpectedly did not find a match for call id").that(call).isNotNull();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index ddde385..fc25152 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -484,6 +484,7 @@
// Sensor reads 100 lux. We should get max brightness.
listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getAutomaticScreenBrightness(), 0.0f);
+ assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getRawAutomaticScreenBrightness(), 0.0f);
// Apply throttling and notify ABC (simulates DisplayPowerController#updatePowerState())
final float throttledBrightness = 0.123f;
@@ -494,6 +495,8 @@
0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
/* shouldResetShortTermModel= */ true);
assertEquals(throttledBrightness, mController.getAutomaticScreenBrightness(), 0.0f);
+ // The raw brightness value should not have throttling applied
+ assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getRawAutomaticScreenBrightness(), 0.0f);
// Remove throttling and notify ABC again
when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
@@ -503,6 +506,7 @@
0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
/* shouldResetShortTermModel= */ true);
assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getAutomaticScreenBrightness(), 0.0f);
+ assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getRawAutomaticScreenBrightness(), 0.0f);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 91fd76e..4bc8242 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -32,6 +32,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
+import com.android.server.display.config.ThermalStatus;
import org.junit.Before;
import org.junit.Test;
@@ -43,6 +44,8 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -164,6 +167,51 @@
assertArrayEquals(new int[]{-1, 10, 20, 30, 40},
mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux());
+ List<DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel> throttlingLevels =
+ new ArrayList();
+ throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.light), 0.4f
+ ));
+ throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.moderate), 0.3f
+ ));
+ throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.severe), 0.2f
+ ));
+ throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.critical), 0.1f
+ ));
+ throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.emergency), 0.05f
+ ));
+ throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.shutdown), 0.025f
+ ));
+ assertEquals(new DisplayDeviceConfig.BrightnessThrottlingData(throttlingLevels),
+ mDisplayDeviceConfig.getBrightnessThrottlingData());
+
+ throttlingLevels.clear();
+ throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.light), 0.2f
+ ));
+ throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.moderate), 0.15f
+ ));
+ throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.severe), 0.1f
+ ));
+ throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.critical), 0.05f
+ ));
+ throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.emergency), 0.025f
+ ));
+ throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.shutdown), 0.0125f
+ ));
+ assertEquals(new DisplayDeviceConfig.BrightnessThrottlingData(throttlingLevels),
+ mDisplayDeviceConfig.getConcurrentDisplaysBrightnessThrottlingData());
+
// Todo: Add asserts for BrightnessThrottlingData, DensityMapping,
// HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor.
}
@@ -428,16 +476,60 @@
+ "<ambientLightHorizonShort>50</ambientLightHorizonShort>\n"
+ "<screenBrightnessRampIncreaseMaxMillis>"
+ "2000"
- + "</screenBrightnessRampIncreaseMaxMillis>\n"
+ + "</screenBrightnessRampIncreaseMaxMillis>\n"
+ "<thermalThrottling>\n"
+ "<brightnessThrottlingMap>\n"
+ "<brightnessThrottlingPoint>\n"
+ + "<thermalStatus>light</thermalStatus>\n"
+ + "<brightness>0.4</brightness>\n"
+ + "</brightnessThrottlingPoint>\n"
+ + "<brightnessThrottlingPoint>\n"
+ + "<thermalStatus>moderate</thermalStatus>\n"
+ + "<brightness>0.3</brightness>\n"
+ + "</brightnessThrottlingPoint>\n"
+ + "<brightnessThrottlingPoint>\n"
+ + "<thermalStatus>severe</thermalStatus>\n"
+ + "<brightness>0.2</brightness>\n"
+ + "</brightnessThrottlingPoint>\n"
+ + "<brightnessThrottlingPoint>\n"
+ + "<thermalStatus>critical</thermalStatus>\n"
+ + "<brightness>0.1</brightness>\n"
+ + "</brightnessThrottlingPoint>\n"
+ + "<brightnessThrottlingPoint>\n"
+ "<thermalStatus>emergency</thermalStatus>\n"
- + "<!-- Throttling to 250 nits: (250-2.0)/(500-2.0)*(0.62-0.0)+0"
- + ".0 = 0.30875502 -->\n"
- + "<brightness>0.30875502</brightness>\n"
+ + "<brightness>0.05</brightness>\n"
+ + "</brightnessThrottlingPoint>\n"
+ + "<brightnessThrottlingPoint>\n"
+ + "<thermalStatus>shutdown</thermalStatus>\n"
+ + "<brightness>0.025</brightness>\n"
+ "</brightnessThrottlingPoint>\n"
+ "</brightnessThrottlingMap>\n"
+ + "<concurrentDisplaysBrightnessThrottlingMap>\n"
+ + "<brightnessThrottlingPoint>\n"
+ + "<thermalStatus>light</thermalStatus>\n"
+ + "<brightness>0.2</brightness>\n"
+ + "</brightnessThrottlingPoint>\n"
+ + "<brightnessThrottlingPoint>\n"
+ + "<thermalStatus>moderate</thermalStatus>\n"
+ + "<brightness>0.15</brightness>\n"
+ + "</brightnessThrottlingPoint>\n"
+ + "<brightnessThrottlingPoint>\n"
+ + "<thermalStatus>severe</thermalStatus>\n"
+ + "<brightness>0.1</brightness>\n"
+ + "</brightnessThrottlingPoint>\n"
+ + "<brightnessThrottlingPoint>\n"
+ + "<thermalStatus>critical</thermalStatus>\n"
+ + "<brightness>0.05</brightness>\n"
+ + "</brightnessThrottlingPoint>\n"
+ + "<brightnessThrottlingPoint>\n"
+ + "<thermalStatus>emergency</thermalStatus>\n"
+ + "<brightness>0.025</brightness>\n"
+ + "</brightnessThrottlingPoint>\n"
+ + "<brightnessThrottlingPoint>\n"
+ + "<thermalStatus>shutdown</thermalStatus>\n"
+ + "<brightness>0.0125</brightness>\n"
+ + "</brightnessThrottlingPoint>\n"
+ + "</concurrentDisplaysBrightnessThrottlingMap>\n"
+ "</thermalThrottling>\n"
+ "<refreshRate>\n"
+ "<lowerBlockingZoneConfigs>\n"
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index ea5caa8..cc1100b 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -55,6 +55,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.ArrayUtils;
+import com.android.security.SecureBox;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
index 19a606e..1cf4471 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
@@ -28,6 +28,8 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.ArrayUtils;
+import com.android.security.SecureBox;
+
import com.google.common.collect.ImmutableMap;
import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 1b983f0b..bb79fd8 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -60,6 +60,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.widget.LockPatternUtils;
+import com.android.security.SecureBox;
import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage;
import com.android.server.locksettings.recoverablekeystore.storage.CleanupManager;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorageTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorageTest.java
index a1cf128..05d30ed 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorageTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorageTest.java
@@ -23,7 +23,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.server.locksettings.recoverablekeystore.SecureBox;
+import com.android.security.SecureBox;
import org.junit.Before;
import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
index ade1bd4..2675f05 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
@@ -66,6 +66,7 @@
.setCrossProfileIntentResolutionStrategy(0)
.setMediaSharedWithParent(false)
.setCredentialShareableWithParent(true)
+ .setDeleteAppWithParent(false)
.build();
final UserProperties actualProps = new UserProperties(defaultProps);
actualProps.setShowInLauncher(14);
@@ -76,6 +77,7 @@
actualProps.setCrossProfileIntentResolutionStrategy(1);
actualProps.setMediaSharedWithParent(true);
actualProps.setCredentialShareableWithParent(false);
+ actualProps.setDeleteAppWithParent(true);
// Write the properties to xml.
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -116,12 +118,14 @@
.setShowInSettings(3452)
.setInheritDevicePolicy(1732)
.setMediaSharedWithParent(true)
+ .setDeleteAppWithParent(true)
.build();
final UserProperties orig = new UserProperties(defaultProps);
orig.setShowInLauncher(2841);
orig.setStartWithParent(false);
orig.setShowInSettings(1437);
orig.setInheritDevicePolicy(9456);
+ orig.setDeleteAppWithParent(false);
// Test every permission level. (Currently, it's linear so it's easy.)
for (int permLevel = 0; permLevel < 4; permLevel++) {
@@ -163,6 +167,8 @@
copy::getCrossProfileIntentFilterAccessControl, exposeAll);
assertEqualGetterOrThrows(orig::getCrossProfileIntentResolutionStrategy,
copy::getCrossProfileIntentResolutionStrategy, exposeAll);
+ assertEqualGetterOrThrows(orig::getDeleteAppWithParent,
+ copy::getDeleteAppWithParent, exposeAll);
// Items requiring hasManagePermission - put them here using hasManagePermission.
assertEqualGetterOrThrows(orig::getShowInSettings, copy::getShowInSettings,
@@ -227,5 +233,6 @@
.isEqualTo(actual.isMediaSharedWithParent());
assertThat(expected.isCredentialShareableWithParent())
.isEqualTo(actual.isCredentialShareableWithParent());
+ assertThat(expected.getDeleteAppWithParent()).isEqualTo(actual.getDeleteAppWithParent());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index 702059d..ff9a79e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -88,7 +88,11 @@
.setCrossProfileIntentFilterAccessControl(10)
.setCrossProfileIntentResolutionStrategy(1)
.setMediaSharedWithParent(true)
- .setCredentialShareableWithParent(false);
+ .setCredentialShareableWithParent(false)
+ .setShowInSettings(900)
+ .setInheritDevicePolicy(340)
+ .setDeleteAppWithParent(true);
+
final UserTypeDetails type = new UserTypeDetails.Builder()
.setName("a.name")
.setEnabled(1)
@@ -152,6 +156,10 @@
.getCrossProfileIntentResolutionStrategy());
assertTrue(type.getDefaultUserPropertiesReference().isMediaSharedWithParent());
assertFalse(type.getDefaultUserPropertiesReference().isCredentialShareableWithParent());
+ assertEquals(900, type.getDefaultUserPropertiesReference().getShowInSettings());
+ assertEquals(340, type.getDefaultUserPropertiesReference()
+ .getInheritDevicePolicy());
+ assertTrue(type.getDefaultUserPropertiesReference().getDeleteAppWithParent());
assertEquals(23, type.getBadgeLabel(0));
assertEquals(24, type.getBadgeLabel(1));
@@ -287,7 +295,11 @@
.setCrossProfileIntentFilterAccessControl(10)
.setCrossProfileIntentResolutionStrategy(1)
.setMediaSharedWithParent(false)
- .setCredentialShareableWithParent(true);
+ .setCredentialShareableWithParent(true)
+ .setShowInSettings(20)
+ .setInheritDevicePolicy(21)
+ .setDeleteAppWithParent(true);
+
final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
builders.put(userTypeAosp1, new UserTypeDetails.Builder()
.setName(userTypeAosp1)
@@ -323,6 +335,10 @@
assertFalse(aospType.getDefaultUserPropertiesReference().isMediaSharedWithParent());
assertTrue(aospType.getDefaultUserPropertiesReference()
.isCredentialShareableWithParent());
+ assertEquals(20, aospType.getDefaultUserPropertiesReference().getShowInSettings());
+ assertEquals(21, aospType.getDefaultUserPropertiesReference()
+ .getInheritDevicePolicy());
+ assertTrue(aospType.getDefaultUserPropertiesReference().getDeleteAppWithParent());
// userTypeAosp2 should be modified.
aospType = builders.get(userTypeAosp2).createUserTypeDetails();
@@ -362,6 +378,11 @@
assertTrue(aospType.getDefaultUserPropertiesReference().isMediaSharedWithParent());
assertFalse(aospType.getDefaultUserPropertiesReference()
.isCredentialShareableWithParent());
+ assertEquals(23, aospType.getDefaultUserPropertiesReference().getShowInSettings());
+ assertEquals(450, aospType.getDefaultUserPropertiesReference()
+ .getInheritDevicePolicy());
+ assertFalse(aospType.getDefaultUserPropertiesReference()
+ .getDeleteAppWithParent());
// userTypeOem1 should be created.
UserTypeDetails.Builder customType = builders.get(userTypeOem1);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index ac5bcff..9b9cb4d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -192,6 +192,7 @@
.isEqualTo(cloneUserProperties.isMediaSharedWithParent());
assertThat(typeProps.isCredentialShareableWithParent())
.isEqualTo(cloneUserProperties.isCredentialShareableWithParent());
+ assertThrows(SecurityException.class, cloneUserProperties::getDeleteAppWithParent);
// Verify clone user parent
assertThat(mUserManager.getProfileParent(mainUserId)).isNull();
@@ -844,8 +845,10 @@
assertThrows(SecurityException.class, userProps::getInheritDevicePolicy);
assertThat(userProps.isMediaSharedWithParent()).isFalse();
assertThat(userProps.isCredentialShareableWithParent()).isTrue();
+ assertThrows(SecurityException.class, userProps::getDeleteAppWithParent);
}
+
// Make sure only max managed profiles can be created
@MediumTest
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index e663245..9fda204 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1475,7 +1475,6 @@
// Make keyguard locked and set the top activity show-when-locked.
KeyguardController keyguardController = activity.mTaskSupervisor.getKeyguardController();
int displayId = activity.getDisplayId();
- doReturn(true).when(keyguardController).isKeyguardLocked(eq(displayId));
final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
topActivity.setVisibleRequested(true);
topActivity.nowVisible = true;
@@ -1485,18 +1484,24 @@
anyBoolean() /* preserveWindows */, anyBoolean() /* notifyClients */);
topActivity.setShowWhenLocked(true);
- // Verify the stack-top activity is occluded keyguard.
- assertEquals(topActivity, task.topRunningActivity());
- assertTrue(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY));
+ try {
+ keyguardController.setKeyguardShown(displayId, true, false);
- // Finish the top activity
- topActivity.setState(PAUSED, "true");
- topActivity.finishing = true;
- topActivity.completeFinishing("test");
+ // Verify the stack-top activity is occluded keyguard.
+ assertEquals(topActivity, task.topRunningActivity());
+ assertTrue(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY));
- // Verify new top activity does not occlude keyguard.
- assertEquals(activity, task.topRunningActivity());
- assertFalse(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY));
+ // Finish the top activity
+ topActivity.setState(PAUSED, "true");
+ topActivity.finishing = true;
+ topActivity.completeFinishing("test");
+
+ // Verify new top activity does not occlude keyguard.
+ assertEquals(activity, task.topRunningActivity());
+ assertFalse(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY));
+ } finally {
+ keyguardController.setKeyguardShown(displayId, false, false);
+ }
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 3dcae91..305260b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -252,7 +252,12 @@
mAtm.setLockScreenShown(true, true);
mRootWindowContainer.forAllDisplays(displayContent -> {
assertTrue(displayContent.isKeyguardLocked());
- assertTrue(displayContent.isAodShowing());
+ // Only default display supports AOD.
+ if (displayContent.isDefaultDisplay) {
+ assertTrue(displayContent.isAodShowing());
+ } else {
+ assertFalse(displayContent.isAodShowing());
+ }
});
// Check setLockScreenShown unlocking both displays
diff --git a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
index 4070bed..28c2d59 100644
--- a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
+++ b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
@@ -115,6 +115,10 @@
@Deprecated
default void onPublishUpdated(int reasonCode, @NonNull String reasonPhrase,
int reasonHeaderCause, @NonNull String reasonHeaderText) throws ImsException {
+ onPublishUpdated(new SipDetails.Builder(SipDetails.METHOD_PUBLISH)
+ .setSipResponseCode(reasonCode, reasonPhrase)
+ .setSipResponseReasonHeader(reasonHeaderCause, reasonHeaderText)
+ .build());
}
/**
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index 3e1c9ef..66442a6 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -27,6 +27,7 @@
import android.telephony.ims.SipDetails;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.RcsFeature;
+import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -205,6 +206,12 @@
* when the Telephony stack has crashed.
*/
default void onNetworkResponse(@NonNull SipDetails details) throws ImsException {
+ if (TextUtils.isEmpty(details.getReasonHeaderText())) {
+ onNetworkResponse(details.getResponseCode(), details.getResponsePhrase());
+ } else {
+ onNetworkResponse(details.getResponseCode(), details.getResponsePhrase(),
+ details.getReasonHeaderCause(), details.getReasonHeaderText());
+ }
}
}
@@ -338,7 +345,14 @@
* {@link RcsFeature} has not received the {@link ImsFeature#onFeatureReady()} callback.
* This may also happen in rare cases when the Telephony stack has crashed.
*/
- default void onNetworkResponse(@NonNull SipDetails details) throws ImsException {};
+ default void onNetworkResponse(@NonNull SipDetails details) throws ImsException {
+ if (TextUtils.isEmpty(details.getReasonHeaderText())) {
+ onNetworkResponse(details.getResponseCode(), details.getResponsePhrase());
+ } else {
+ onNetworkResponse(details.getResponseCode(), details.getResponsePhrase(),
+ details.getReasonHeaderCause(), details.getReasonHeaderText());
+ }
+ };
/**
* Notify the framework of the latest XML PIDF documents included in the network response